diff --git a/.gitignore b/.gitignore index 345c6b9..6de202c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/LVM2.2.02.166.tgz +SOURCES/LVM2.2.02.171.tgz diff --git a/.lvm2.metadata b/.lvm2.metadata index 59744c7..7d21c07 100644 --- a/.lvm2.metadata +++ b/.lvm2.metadata @@ -1 +1 @@ -5e2dd5bcae8a55c1721bf5c24e8c263895818b44 SOURCES/LVM2.2.02.166.tgz +134498350084fe1371e48e1bdcf558913a112a78 SOURCES/LVM2.2.02.171.tgz diff --git a/SOURCES/lvm2-2_02_167-disable-lvconvert-of-thin-pool-to-raid-while-active.patch b/SOURCES/lvm2-2_02_167-disable-lvconvert-of-thin-pool-to-raid-while-active.patch deleted file mode 100644 index 3301d1b..0000000 --- a/SOURCES/lvm2-2_02_167-disable-lvconvert-of-thin-pool-to-raid-while-active.patch +++ /dev/null @@ -1,32 +0,0 @@ - WHATS_NEW | 4 ++++ - lib/metadata/raid_manip.c | 5 +++++ - 2 files changed, 9 insertions(+) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 977e578..5cbf4ec 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,3 +1,7 @@ -+Version 2.02.167 - -+====================================== -+ Disable lvconvert of thin pool to raid while active. -+ - Version 2.02.166 - 26th September 2016 - ====================================== - Fix lvm2-activation-generator to read all LVM2 config sources. (2.02.155) -diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c -index e5fdf4f..5fc520e 100644 ---- a/lib/metadata/raid_manip.c -+++ b/lib/metadata/raid_manip.c -@@ -776,6 +776,11 @@ static int _raid_add_images_without_commit(struct logical_volume *lv, - return 0; - } - -+ if (lv_is_active(lv_lock_holder(lv)) && (old_count == 1) && (lv_is_thin_pool_data(lv) || lv_is_thin_pool_metadata(lv))) { -+ log_error("Can't add image to active thin pool LV %s yet. Deactivate first.", display_lvname(lv)); -+ return 0; -+ } -+ - if (!archive(lv->vg)) - return_0; - diff --git a/SOURCES/lvm2-2_02_167-fix-inability-of-lvconvert-repair-for-cache-raid-volumes.patch b/SOURCES/lvm2-2_02_167-fix-inability-of-lvconvert-repair-for-cache-raid-volumes.patch deleted file mode 100644 index 8dc2754..0000000 --- a/SOURCES/lvm2-2_02_167-fix-inability-of-lvconvert-repair-for-cache-raid-volumes.patch +++ /dev/null @@ -1,27 +0,0 @@ -@@ -, +, @@ - convert internal LV" ---- - tools/lvconvert.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) ---- a/tools/lvconvert.c -+++ a/tools/lvconvert.c -@@ -243,6 +243,7 @@ static int _lvconvert_name_params(struct lvconvert_params *lp, - !strstr(lp->lv_name, "_tmeta") && - !strstr(lp->lv_name, "_cdata") && - !strstr(lp->lv_name, "_cmeta") && -+ !strstr(lp->lv_name, "_corig") && - !apply_lvname_restrictions(lp->lv_name)) - return_0; - -@@ -4440,7 +4441,8 @@ static int _lvconvert(struct cmd_context *cmd, struct logical_volume *lv, - !lv_is_thin_pool_metadata(lv) && - !lv_is_thin_pool_data(lv) && - !lv_is_used_cache_pool(lv) && -- !lv_is_raid_image(lv)) { -+ !lv_is_raid_image(lv) && -+ !(lv_is_raid(lv) && strstr(lv->name, "_corig"))) { - log_error("Cannot convert internal LV %s.", display_lvname(lv)); - ret = 0; - goto out; --- - diff --git a/SOURCES/lvm2-2_02_167-fix-raid4-coversion-from-striped.patch b/SOURCES/lvm2-2_02_167-fix-raid4-coversion-from-striped.patch deleted file mode 100644 index 11dd7db..0000000 --- a/SOURCES/lvm2-2_02_167-fix-raid4-coversion-from-striped.patch +++ /dev/null @@ -1,532 +0,0 @@ - WHATS_NEW | 2 + - lib/metadata/raid_manip.c | 277 +++++++++++++++++++++++++++++++--- - test/shell/lvconvert-raid-takeover.sh | 53 ++++++- - tools/lvconvert.c | 24 +-- - 4 files changed, 314 insertions(+), 42 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 5cbf4ec..6a0c311 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,7 @@ - Version 2.02.167 - - ====================================== -+ Add direct striped -> raid4 conversion -+ Fix raid4 parity image pair position on conversions from striped/raid0* - Disable lvconvert of thin pool to raid while active. - - Version 2.02.166 - 26th September 2016 -diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c -index 5fc520e..e5fd195 100644 ---- a/lib/metadata/raid_manip.c -+++ b/lib/metadata/raid_manip.c -@@ -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 */ -- .possible_types = SEG_RAID4, -+ { .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 striped, i.e. seg->area_count > 0 */ -+ { .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 }, /* 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, -- .current_areas = ~0U, -- .options = ALLOW_NONE }, -- { .current_types = SEG_RAID4, -- .possible_types = SEG_STRIPED_TARGET|SEG_RAID0|SEG_RAID0_META, -+ .possible_types = SEG_RAID1, - .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; - } - -diff --git a/test/shell/lvconvert-raid-takeover.sh b/test/shell/lvconvert-raid-takeover.sh -index 19a65d3..0140e22 100644 ---- a/test/shell/lvconvert-raid-takeover.sh -+++ b/test/shell/lvconvert-raid-takeover.sh -@@ -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 -diff --git a/tools/lvconvert.c b/tools/lvconvert.c -index 0d2a4d1..541df72 100644 ---- a/tools/lvconvert.c -+++ b/tools/lvconvert.c -@@ -1931,7 +1931,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; - } -@@ -2017,28 +2017,18 @@ 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; -- -- log_print_unless_silent("Logical volume %s successfully converted.", -- display_lvname(lv)); -- return 1; -- } -+ 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; - -- log_error("Conversion operation not yet supported."); -- return 0; -+ log_print_unless_silent("Logical volume %s successfully converted.", -+ display_lvname(lv)); -+ return 1; - } - - static int _lvconvert_splitsnapshot(struct cmd_context *cmd, struct logical_volume *cow, diff --git a/SOURCES/lvm2-2_02_167-prevent-raid4-creation-and-conversion-on-non-supporting-kernels.patch b/SOURCES/lvm2-2_02_167-prevent-raid4-creation-and-conversion-on-non-supporting-kernels.patch deleted file mode 100644 index 93eb82e..0000000 --- a/SOURCES/lvm2-2_02_167-prevent-raid4-creation-and-conversion-on-non-supporting-kernels.patch +++ /dev/null @@ -1,303 +0,0 @@ - WHATS_NEW | 1 + - lib/activate/activate.c | 25 +++++++++++++++++++++++++ - lib/activate/activate.h | 3 ++- - lib/metadata/lv.c | 16 +++++++++++++++- - lib/metadata/segtype.h | 3 ++- - lib/raid/raid.c | 10 ++++++++-- - test/shell/lvconvert-raid-takeover.sh | 13 +++++++++++++ - tools/lvconvert.c | 26 ++++++++++++++++++++++++++ - tools/lvcreate.c | 6 ++++++ - 9 files changed, 98 insertions(+), 5 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 6a0c311..519bbc9 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,6 @@ - Version 2.02.167 - - ====================================== -+ Prevent raid4 creation/conversion on non-supporting kernels - Add direct striped -> raid4 conversion - Fix raid4 parity image pair position on conversions from striped/raid0* - Disable lvconvert of thin pool to raid while active. -diff --git a/lib/activate/activate.c b/lib/activate/activate.c -index 5550955..571f2b2 100644 ---- a/lib/activate/activate.c -+++ b/lib/activate/activate.c -@@ -370,6 +370,11 @@ void activation_exit(void) - { - } - -+int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype) -+{ -+ return 1; -+} -+ - int lv_is_active(const struct logical_volume *lv) - { - return 0; -@@ -1489,6 +1494,26 @@ out: - return r || l; - } - -+/* -+ * Check if "raid4" @segtype is supported by kernel. -+ * -+ * if segment type is not raid4, return 1. -+ */ -+int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype) -+{ -+ unsigned attrs; -+ -+ if (segtype_is_raid4(segtype) && -+ (!segtype->ops->target_present || -+ !segtype->ops->target_present(cmd, NULL, &attrs) || -+ !(attrs & RAID_FEATURE_RAID4))) { -+ log_error("RAID module does not support RAID4."); -+ return 0; -+ } -+ -+ return 1; -+} -+ - int lv_is_active(const struct logical_volume *lv) - { - return _lv_is_active(lv, NULL, NULL, NULL); -diff --git a/lib/activate/activate.h b/lib/activate/activate.h -index 1e8d7a8..3922d78 100644 ---- a/lib/activate/activate.h -+++ b/lib/activate/activate.h -@@ -1,6 +1,6 @@ - /* - * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -- * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. -+ * Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * -@@ -99,6 +99,7 @@ int target_present(struct cmd_context *cmd, const char *target_name, - int use_modprobe); - int target_version(const char *target_name, uint32_t *maj, - uint32_t *min, uint32_t *patchlevel); -+int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype); - int lvm_dm_prefix_check(int major, int minor, const char *prefix); - int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg, - struct dm_list *modules); -diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c -index 53a1044..0f1b6e7 100644 ---- a/lib/metadata/lv.c -+++ b/lib/metadata/lv.c -@@ -1,6 +1,6 @@ - /* - * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -- * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. -+ * Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * -@@ -1425,6 +1425,7 @@ int lv_active_change(struct cmd_context *cmd, struct logical_volume *lv, - enum activation_change activate, int needs_exclusive) - { - const char *ay_with_mode = NULL; -+ struct lv_segment *seg = first_seg(lv); - - if (activate == CHANGE_ASY) - ay_with_mode = "sh"; -@@ -1461,6 +1462,9 @@ deactivate: - break; - case CHANGE_ALY: - case CHANGE_AAY: -+ if (!raid4_is_supported(cmd, seg->segtype)) -+ goto no_raid4; -+ - if (needs_exclusive || _lv_is_exclusive(lv)) { - log_verbose("Activating logical volume %s exclusively locally.", - display_lvname(lv)); -@@ -1475,6 +1479,9 @@ deactivate: - break; - case CHANGE_AEY: - exclusive: -+ if (!raid4_is_supported(cmd, seg->segtype)) -+ goto no_raid4; -+ - log_verbose("Activating logical volume %s exclusively.", - display_lvname(lv)); - if (!activate_lv_excl(cmd, lv)) -@@ -1483,6 +1490,9 @@ exclusive: - case CHANGE_ASY: - case CHANGE_AY: - default: -+ if (!raid4_is_supported(cmd, seg->segtype)) -+ goto no_raid4; -+ - if (needs_exclusive || _lv_is_exclusive(lv)) - goto exclusive; - log_verbose("Activating logical volume %s.", display_lvname(lv)); -@@ -1495,6 +1505,10 @@ exclusive: - log_error("Failed to unlock logical volume %s.", display_lvname(lv)); - - return 1; -+ -+no_raid4: -+ log_error("Failed to activate %s LV %s", lvseg_name(seg), display_lvname(lv)); -+ return 0; - } - - char *lv_active_dup(struct dm_pool *mem, const struct logical_volume *lv) -diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h -index 9ca740d..292b8b6 100644 ---- a/lib/metadata/segtype.h -+++ b/lib/metadata/segtype.h -@@ -1,6 +1,6 @@ - /* - * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -- * Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. -+ * Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * -@@ -268,6 +268,7 @@ struct segment_type *init_unknown_segtype(struct cmd_context *cmd, - #define RAID_FEATURE_RAID10 (1U << 0) /* version 1.3 */ - #define RAID_FEATURE_RAID0 (1U << 1) /* version 1.7 */ - #define RAID_FEATURE_RESHAPING (1U << 2) /* version 1.8 */ -+#define RAID_FEATURE_RAID4 (1U << 3) /* ! version 1.8 or 1.9.0 */ - - #ifdef RAID_INTERNAL - int init_raid_segtypes(struct cmd_context *cmd, struct segtype_library *seglib); -diff --git a/lib/raid/raid.c b/lib/raid/raid.c -index 3bc3c75..92a96a3 100644 ---- a/lib/raid/raid.c -+++ b/lib/raid/raid.c -@@ -1,5 +1,5 @@ - /* -- * Copyright (C) 2011-2013 Red Hat, Inc. All rights reserved. -+ * Copyright (C) 2011-2016 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * -@@ -366,7 +366,7 @@ static int _raid_target_present(struct cmd_context *cmd, - - static int _raid_checked = 0; - static int _raid_present = 0; -- static int _raid_attrs = 0; -+ static unsigned _raid_attrs = 0; - uint32_t maj, min, patchlevel; - unsigned i; - -@@ -389,6 +389,12 @@ static int _raid_target_present(struct cmd_context *cmd, - else - log_very_verbose("Target raid does not support %s.", - _features[i].feature); -+ -+ if (!(maj == 1 && (min == 8 || (min == 9 && patchlevel == 0)))) -+ _raid_attrs |= RAID_FEATURE_RAID4; -+ else -+ log_very_verbose("Target raid does not support %s.", -+ SEG_TYPE_NAME_RAID4); - } - - if (attributes) -diff --git a/test/shell/lvconvert-raid-takeover.sh b/test/shell/lvconvert-raid-takeover.sh -index 0140e22..332786d 100644 ---- a/test/shell/lvconvert-raid-takeover.sh -+++ b/test/shell/lvconvert-raid-takeover.sh -@@ -16,6 +16,8 @@ SKIP_WITH_LVMPOLLD=1 - - aux have_raid 1 9 0 || skip - -+[ `aux have_raid 1.9.1` ] && correct_raid4_layout=1 -+ - aux prepare_vg 9 288 - - # Delay 1st leg so that rebuilding status characters -@@ -99,6 +101,9 @@ check lv_field $vg/$lv3 stripes 3 - echo y | mkfs -t ext4 /dev/mapper/$vg-$lv3 - fsck -fn /dev/mapper/$vg-$lv3 - -+if [ $correct_raid4_layout -eq 1 ] -+then -+ - # Create 3-way raid4 - lvcreate -y -aey --type raid4 -i 3 -L 64M -n $lv4 $vg - check lv_field $vg/$lv4 segtype "raid4" -@@ -164,4 +169,12 @@ check lv_field $vg/$lv1 segtype "striped" - check lv_field $vg/$lv1 stripes 3 - fsck -fn /dev/mapper/$vg-$lv1 - -+else -+ -+not lvcreate -y -aey --type raid4 -i 3 -L 64M -n $lv4 $vg -+not lvconvert -y --ty raid4 $vg/$lv1 -+not lvconvert -y --ty raid4 $vg/$lv2 -+ -+fi -+ - vgremove -ff $vg -diff --git a/tools/lvconvert.c b/tools/lvconvert.c -index 541df72..7a4215a 100644 ---- a/tools/lvconvert.c -+++ b/tools/lvconvert.c -@@ -1821,6 +1821,25 @@ static void _lvconvert_raid_repair_ask(struct cmd_context *cmd, - } - } - -+/* Check for dm-raid target supporting raid4 conversion properly. */ -+static int _raid4_conversion_supported(struct logical_volume *lv, struct lvconvert_params *lp) -+{ -+ int ret = 1; -+ struct lv_segment *seg = first_seg(lv); -+ -+ if (seg_is_raid4(seg)) -+ ret = raid4_is_supported(lv->vg->cmd, seg->segtype); -+ else if (segtype_is_raid4(lp->segtype)) -+ ret = raid4_is_supported(lv->vg->cmd, lp->segtype); -+ -+ if (ret) -+ return 1; -+ -+ log_error("Cannot convert %s LV %s to %s.", -+ lvseg_name(seg), display_lvname(lv), lp->segtype->name); -+ return 0; -+} -+ - static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *lp) - { - int replace = 0, image_count = 0; -@@ -1945,6 +1964,9 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l - return 0; - } - -+ if (!_raid4_conversion_supported(lv, lp)) -+ return 0; -+ - if (!arg_is_set(cmd, stripes_long_ARG)) - lp->stripes = 0; - -@@ -2018,6 +2040,10 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l - } - - try_new_takeover_or_reshape: -+ -+ if (!_raid4_conversion_supported(lv, lp)) -+ return 0; -+ - /* FIXME This needs changing globally. */ - if (!arg_is_set(cmd, stripes_long_ARG)) - lp->stripes = 0; -diff --git a/tools/lvcreate.c b/tools/lvcreate.c -index 387c8d4..dbc0708 100644 ---- a/tools/lvcreate.c -+++ b/tools/lvcreate.c -@@ -1054,6 +1054,12 @@ static int _lvcreate_params(struct cmd_context *cmd, - return 0; - } - -+ if (segtype_is_raid4(lp->segtype) && -+ !(lp->target_attr & RAID_FEATURE_RAID4)) { -+ log_error("RAID module does not support RAID4."); -+ return 0; -+ } -+ - if (segtype_is_raid10(lp->segtype) && !(lp->target_attr & RAID_FEATURE_RAID10)) { - log_error("RAID module does not support RAID10."); - return 0; diff --git a/SOURCES/lvm2-2_02_169-clvmd-add-mutex-protection-for-cpg_-call.patch b/SOURCES/lvm2-2_02_169-clvmd-add-mutex-protection-for-cpg_-call.patch deleted file mode 100644 index c724772..0000000 --- a/SOURCES/lvm2-2_02_169-clvmd-add-mutex-protection-for-cpg_-call.patch +++ /dev/null @@ -1,49 +0,0 @@ -From dae4f53acb269219e876c229c8f034fcdaf3ff5a Mon Sep 17 00:00:00 2001 -From: Zdenek Kabelac -Date: Sat, 4 Feb 2017 14:47:27 +0100 -Subject: [PATCH] clvmd: add mutex protection for cpg_ call - -The library for corosync multicasting is not supporting multithread -usage - add local mutex to avoid parallel call of cpg_mcast_joined(). ---- - WHATS_NEW | 1 + - daemons/clvmd/clvmd-corosync.c | 4 ++++ - 2 files changed, 5 insertions(+) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 8ae2df8..0b571ae 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,6 @@ - Version 2.02.169 - - ===================================== -+ Avoid parallel usage of cpg_mcast_joined() in clvmd with corosync. - Fix segfault in lvmetad from missing NULL in daemon_reply_simple. - - Version 2.02.167 - -diff --git a/daemons/clvmd/clvmd-corosync.c b/daemons/clvmd/clvmd-corosync.c -index 05c9882..2227cbf 100644 ---- a/daemons/clvmd/clvmd-corosync.c -+++ b/daemons/clvmd/clvmd-corosync.c -@@ -532,6 +532,7 @@ static int _cluster_fd_callback(struct local_client *fd, char *buf, int len, - static int _cluster_send_message(const void *buf, int msglen, const char *csid, - const char *errtext) - { -+ static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER; - struct iovec iov[2]; - cs_error_t err; - int target_node; -@@ -546,7 +547,10 @@ static int _cluster_send_message(const void *buf, int msglen, const char *csid, - iov[1].iov_base = (char *)buf; - iov[1].iov_len = msglen; - -+ pthread_mutex_lock(&_mutex); - err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2); -+ pthread_mutex_unlock(&_mutex); -+ - return cs_to_errno(err); - } - --- -1.8.3.1 - diff --git a/SOURCES/lvm2-2_02_169-fix-segfault-in-lvmetad-from-missing-null-in-daemon_reply_simple.patch b/SOURCES/lvm2-2_02_169-fix-segfault-in-lvmetad-from-missing-null-in-daemon_reply_simple.patch deleted file mode 100644 index 21b85a4..0000000 --- a/SOURCES/lvm2-2_02_169-fix-segfault-in-lvmetad-from-missing-null-in-daemon_reply_simple.patch +++ /dev/null @@ -1,50 +0,0 @@ - WHATS_NEW | 4 ++++ - daemons/lvmetad/lvmetad-core.c | 9 ++++++--- - 2 files changed, 10 insertions(+), 3 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 519bbc9..8ae2df8 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,3 +1,7 @@ -+Version 2.02.169 - -+===================================== -+ Fix segfault in lvmetad from missing NULL in daemon_reply_simple. -+ - Version 2.02.167 - - ====================================== - Prevent raid4 creation/conversion on non-supporting kernels -diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c -index 36a5cec..c46c7ac 100644 ---- a/daemons/lvmetad/lvmetad-core.c -+++ b/daemons/lvmetad/lvmetad-core.c -@@ -2745,7 +2745,8 @@ static response handler(daemon_state s, client_handle h, request r) - "expected = %s", state->token, - "received = %s", token, - "update_pid = " FMTd64, (int64_t)state->update_pid, -- "reason = %s", "another command has populated the cache"); -+ "reason = %s", "another command has populated the cache", -+ NULL); - } - - DEBUGLOG(state, "token_update end len %d pid %d new token %s", -@@ -2778,7 +2779,8 @@ static response handler(daemon_state s, client_handle h, request r) - "expected = %s", state->token, - "received = %s", token, - "update_pid = " FMTd64, (int64_t)state->update_pid, -- "reason = %s", "another command has populated the cache"); -+ "reason = %s", "another command has populated the cache", -+ NULL); - } - - /* If a pid doing update was cancelled, ignore its update messages. */ -@@ -2793,7 +2795,8 @@ static response handler(daemon_state s, client_handle h, request r) - "expected = %s", state->token, - "received = %s", token, - "update_pid = " FMTd64, (int64_t)state->update_pid, -- "reason = %s", "another command has populated the lvmetad cache"); -+ "reason = %s", "another command has populated the lvmetad cache", -+ NULL); - } - - pthread_mutex_unlock(&state->token_lock); diff --git a/SOURCES/lvm2-2_02_169-lvcreate-fix-striped-limit.patch b/SOURCES/lvm2-2_02_169-lvcreate-fix-striped-limit.patch deleted file mode 100644 index 9e224dd..0000000 --- a/SOURCES/lvm2-2_02_169-lvcreate-fix-striped-limit.patch +++ /dev/null @@ -1,34 +0,0 @@ - WHATS_NEW | 1 + - tools/lvcreate.c | 4 +++- - 2 files changed, 4 insertions(+), 1 deletion(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 0b571ae..26aa5b0 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,6 @@ - Version 2.02.169 - - ===================================== -+ Fix limit of stripes in lvcreate. - Avoid parallel usage of cpg_mcast_joined() in clvmd with corosync. - Fix segfault in lvmetad from missing NULL in daemon_reply_simple. - -diff --git a/tools/lvcreate.c b/tools/lvcreate.c -index dbc0708..dae9da6 100644 ---- a/tools/lvcreate.c -+++ b/tools/lvcreate.c -@@ -559,8 +559,10 @@ static int _read_mirror_and_raid_params(struct cmd_context *cmd, - else if (seg_is_any_raid6(lp)) - max_images -= 2; - } -- } else -+ } else if (seg_is_mirrored(lp)) - max_images = DEFAULT_MIRROR_MAX_IMAGES; -+ else -+ max_images = MAX_STRIPES; - - /* Common mirror and raid params */ - if (arg_is_set(cmd, mirrors_ARG)) { --- -1.8.3.1 - diff --git a/SOURCES/lvm2-2_02_172-fix-raid-segfault.patch b/SOURCES/lvm2-2_02_172-fix-raid-segfault.patch new file mode 100644 index 0000000..0a40151 --- /dev/null +++ b/SOURCES/lvm2-2_02_172-fix-raid-segfault.patch @@ -0,0 +1,16 @@ + lib/metadata/raid_manip.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c +index 9e4f3a3..66fc2bd 100644 +--- a/lib/metadata/raid_manip.c ++++ b/lib/metadata/raid_manip.c +@@ -2373,7 +2373,7 @@ static int _raid_reshape(struct logical_volume *lv, + if (seg->area_count != 2 || old_image_count != seg->area_count) { + if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, &removal_lvs, NULL)) + return_0; +- } if (!_vg_write_commit_backup(lv->vg)) ++ } else if (!_vg_write_commit_backup(lv->vg)) + return_0; + + return 1; diff --git a/SOURCES/lvm2-2_02_172-libdm-initialization-of-reused-struct.patch b/SOURCES/lvm2-2_02_172-libdm-initialization-of-reused-struct.patch new file mode 100644 index 0000000..05f5a61 --- /dev/null +++ b/SOURCES/lvm2-2_02_172-libdm-initialization-of-reused-struct.patch @@ -0,0 +1,53 @@ + WHATS_NEW_DM | 1 + + libdm/ioctl/libdm-iface.c | 10 +++++++++- + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index 581cd42..bf08130 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,5 +1,6 @@ + Version 1.02.141 - + =============================== ++ Fix reusing of dm_task structure for status reading (used by dmeventd). + dm_get_status_raid() handle better some incosistent md statuses. + Accept truncated files in calls to dm_stats_update_regions_from_fd(). + Restore Warning by 5% increment when thin-pool is over 80% (1.02.138). +diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c +index cb3e8dc..65e75f6 100644 +--- a/libdm/ioctl/libdm-iface.c ++++ b/libdm/ioctl/libdm-iface.c +@@ -467,7 +467,7 @@ static void _dm_zfree_dmi(struct dm_ioctl *dmi) + } + } + +-void dm_task_destroy(struct dm_task *dmt) ++static void _dm_task_free_targets(struct dm_task *dmt) + { + struct target *t, *n; + +@@ -478,6 +478,12 @@ void dm_task_destroy(struct dm_task *dmt) + dm_free(t); + } + ++ dmt->head = dmt->tail = NULL; ++} ++ ++void dm_task_destroy(struct dm_task *dmt) ++{ ++ _dm_task_free_targets(dmt); + _dm_zfree_dmi(dmt->dmi.v4); + dm_free(dmt->dev_name); + dm_free(dmt->mangled_dev_name); +@@ -652,6 +658,8 @@ static int _unmarshal_status(struct dm_task *dmt, struct dm_ioctl *dmi) + uint32_t i; + struct dm_target_spec *spec; + ++ _dm_task_free_targets(dmt); ++ + for (i = 0; i < dmi->target_count; i++) { + spec = (struct dm_target_spec *) outptr; + if (!dm_task_add_target(dmt, spec->sector_start, +-- +1.8.3.1 + diff --git a/SOURCES/lvm2-2_02_172-upstream-6.patch b/SOURCES/lvm2-2_02_172-upstream-6.patch new file mode 100644 index 0000000..36ae424 --- /dev/null +++ b/SOURCES/lvm2-2_02_172-upstream-6.patch @@ -0,0 +1,1466 @@ + WHATS_NEW | 2 + + WHATS_NEW_DM | 1 + + lib/metadata/lv.c | 11 +- + lib/metadata/lv_manip.c | 18 +- + lib/metadata/metadata-exported.h | 1 + + lib/metadata/raid_manip.c | 354 +++++++++++---------- + lib/raid/raid.c | 39 +-- + lib/report/report.c | 10 +- + libdm/libdm-targets.c | 18 ++ + scripts/fsadm.sh | 1 + + test/lib/check.sh | 2 +- + test/shell/fsadm.sh | 1 + + test/shell/lvconvert-raid-status-validation.sh | 127 ++++++++ + .../shell/lvconvert-raid-takeover-alloc-failure.sh | 9 +- + test/shell/lvconvert-raid-takeover-thin.sh | 72 +++++ + tools/lvresize.c | 1 + + 16 files changed, 465 insertions(+), 202 deletions(-) +diff --git a/WHATS_NEW b/WHATS_NEW +index b2796f6..e60380a 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,7 @@ + Version 2.02.172 - + =============================== ++ Reenable conversion of data and metadata thin-pool volumes to raid. ++ Improve raid status reporting with lvs. + No longer necessary to '--force' a repair for RAID1 + Linear to RAID1 upconverts now use "recover" sync action, not "resync". + Improve lvcreate --cachepool arg validation. +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index 5718ab7..581cd42 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,5 +1,6 @@ + Version 1.02.141 - + =============================== ++ dm_get_status_raid() handle better some incosistent md statuses. + Accept truncated files in calls to dm_stats_update_regions_from_fd(). + Restore Warning by 5% increment when thin-pool is over 80% (1.02.138). + +diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c +index b24c4aa..c87bb6b 100644 +--- a/lib/metadata/lv.c ++++ b/lib/metadata/lv.c +@@ -395,6 +395,15 @@ dm_percent_t lvseg_percent_with_info_and_seg_status(const struct lv_with_info_an + } + } + break; ++ case SEG_STATUS_RAID: ++ switch (type) { ++ case PERCENT_GET_DIRTY: ++ p = dm_make_percent(s->raid->insync_regions, s->raid->total_regions); ++ break; ++ default: ++ p = DM_PERCENT_INVALID; ++ } ++ break; + case SEG_STATUS_SNAPSHOT: + if (s->snapshot->merge_failed) + p = DM_PERCENT_INVALID; +@@ -1087,7 +1096,7 @@ int lv_raid_healthy(const struct logical_volume *lv) + } + + if (!seg_is_raid(raid_seg)) { +- log_error("%s on %s is not a RAID segment", ++ log_error(INTERNAL_ERROR "%s on %s is not a RAID segment.", + raid_seg->lv->name, lv->name); + return 0; + } +diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c +index 8f38839..c431868 100644 +--- a/lib/metadata/lv_manip.c ++++ b/lib/metadata/lv_manip.c +@@ -4578,6 +4578,7 @@ enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE }; + static int _fsadm_cmd(enum fsadm_cmd_e fcmd, + struct logical_volume *lv, + uint32_t extents, ++ int yes, + int force, + int *status) + { +@@ -4585,7 +4586,7 @@ static int _fsadm_cmd(enum fsadm_cmd_e fcmd, + struct cmd_context *cmd = vg->cmd; + char lv_path[PATH_MAX]; + char size_buf[SIZE_BUF]; +- const char *argv[FSADM_CMD_MAX_ARGS + 2]; ++ const char *argv[FSADM_CMD_MAX_ARGS + 4]; + unsigned i = 0; + + argv[i++] = find_config_tree_str(cmd, global_fsadm_executable_CFG, NULL); +@@ -4596,6 +4597,9 @@ static int _fsadm_cmd(enum fsadm_cmd_e fcmd, + if (verbose_level() >= _LOG_NOTICE) + argv[i++] = "--verbose"; + ++ if (yes) ++ argv[i++] = "--yes"; ++ + if (force) + argv[i++] = "--force"; + +@@ -5498,7 +5502,7 @@ int lv_resize(struct logical_volume *lv, + + if (lp->resizefs) { + if (!lp->nofsck && +- !_fsadm_cmd(FSADM_CMD_CHECK, lv, 0, lp->force, &status)) { ++ !_fsadm_cmd(FSADM_CMD_CHECK, lv, 0, lp->yes, lp->force, &status)) { + if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) { + log_error("Filesystem check failed."); + return 0; +@@ -5508,7 +5512,7 @@ int lv_resize(struct logical_volume *lv, + + /* FIXME forks here */ + if ((lp->resize == LV_REDUCE) && +- !_fsadm_cmd(FSADM_CMD_RESIZE, lv, lp->extents, lp->force, NULL)) { ++ !_fsadm_cmd(FSADM_CMD_RESIZE, lv, lp->extents, lp->yes, lp->force, NULL)) { + log_error("Filesystem resize failed."); + return 0; + } +@@ -5589,7 +5593,7 @@ out: + display_lvname(lv)); + + if (lp->resizefs && (lp->resize == LV_EXTEND) && +- !_fsadm_cmd(FSADM_CMD_RESIZE, lv, lp->extents, lp->force, NULL)) ++ !_fsadm_cmd(FSADM_CMD_RESIZE, lv, lp->extents, lp->yes, lp->force, NULL)) + return_0; + + ret = 1; +@@ -6368,6 +6372,12 @@ static int _lv_update_and_reload(struct logical_volume *lv, int origin_only) + if (!vg_write(vg)) + return_0; + ++ if (lock_lv != lv) { ++ log_debug_activation("Dropping origin_only for %s as lock holds %s", ++ display_lvname(lv), display_lvname(lock_lv)); ++ origin_only = 0; ++ } ++ + if (!(origin_only ? suspend_lv_origin(vg->cmd, lock_lv) : suspend_lv(vg->cmd, lock_lv))) { + log_error("Failed to lock logical volume %s.", + display_lvname(lock_lv)); +diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h +index c4bebd0..6c3d8d7 100644 +--- a/lib/metadata/metadata-exported.h ++++ b/lib/metadata/metadata-exported.h +@@ -649,6 +649,7 @@ struct lvresize_params { + int use_policies; + + alloc_policy_t alloc; ++ int yes; + int force; + int nosync; + int nofsck; +diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c +index ade27e6..9e4f3a3 100644 +--- a/lib/metadata/raid_manip.c ++++ b/lib/metadata/raid_manip.c +@@ -75,6 +75,24 @@ static int _rebuild_with_emptymeta_is_supported(struct cmd_context *cmd, + return 1; + } + ++/* https://bugzilla.redhat.com/1447812 check open count of @lv vs. @open_count */ ++static int _check_lv_open_count(struct logical_volume *lv, int open_count) { ++ struct lvinfo info = { 0 }; ++ ++ if (!lv_info(lv->vg->cmd, lv, 0, &info, 1, 0)) { ++ log_error("lv_info failed: aborting."); ++ return 0; ++ } ++ if (info.open_count != open_count) { ++ log_error("Reshape is only supported when %s is not in use (e.g. unmount filesystem).", ++ display_lvname(lv)); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ + /* + * Ensure region size exceeds the minimum for @lv because + * MD's bitmap is limited to tracking 2^21 regions. +@@ -442,7 +460,7 @@ static int _raid_remove_top_layer(struct logical_volume *lv, + + if (!(lvl_array = dm_pool_alloc(lv->vg->vgmem, 2 * sizeof(*lvl)))) { + log_error("Memory allocation failed."); +- return_0; ++ return 0; + } + + /* Add last metadata area to removal_lvs */ +@@ -534,10 +552,20 @@ static int _lv_update_reload_fns_reset_eliminate_lvs(struct logical_volume *lv, + fn_on_lv_t fn_pre_on_lv = NULL, fn_post_on_lv; + void *fn_pre_data, *fn_post_data = NULL; + struct dm_list *removal_lvs; ++ const struct logical_volume *lock_lv = lv_lock_holder(lv); + + va_start(ap, origin_only); + removal_lvs = va_arg(ap, struct dm_list *); + ++ if (lock_lv != lv) { ++ log_debug_activation("Dropping origin_only for %s as lock holds %s", ++ display_lvname(lv), display_lvname(lock_lv)); ++ origin_only = 0; ++ } ++ ++ /* TODO/FIXME: this function should be simplified to just call ++ * lv_update_and_reload() and cleanup of remained LVs */ ++ + /* Retrieve post/pre functions and post/pre data reference from variable arguments, if any */ + if ((fn_post_on_lv = va_arg(ap, fn_on_lv_t))) { + fn_post_data = va_arg(ap, void *); +@@ -545,11 +573,13 @@ static int _lv_update_reload_fns_reset_eliminate_lvs(struct logical_volume *lv, + fn_pre_data = va_arg(ap, void *); + } + ++ va_end(ap); ++ + /* Call any fn_pre_on_lv before the first update and reload call (e.g. to rename LVs) */ + /* returns 1: ok+ask caller to update, 2: metadata commited+ask caller to resume */ + if (fn_pre_on_lv && !(r = fn_pre_on_lv(lv, fn_pre_data))) { + log_error(INTERNAL_ERROR "Pre callout function failed."); +- goto err; ++ return 0; + } + + if (r == 2) { +@@ -557,19 +587,19 @@ static int _lv_update_reload_fns_reset_eliminate_lvs(struct logical_volume *lv, + * Returning 2 from pre function -> lv is suspended and + * metadata got updated, don't need to do it again + */ +- if (!(r = (origin_only ? resume_lv_origin(lv->vg->cmd, lv_lock_holder(lv)) : +- resume_lv(lv->vg->cmd, lv_lock_holder(lv))))) { ++ if (!(r = (origin_only ? resume_lv_origin(lv->vg->cmd, lock_lv) : ++ resume_lv(lv->vg->cmd, lock_lv)))) { + log_error("Failed to resume %s.", display_lvname(lv)); +- goto err; ++ return 0; + } + + /* Update metadata and reload mappings including flags (e.g. LV_REBUILD, LV_RESHAPE_DELTA_DISKS_PLUS) */ + } else if (!(r = (origin_only ? lv_update_and_reload_origin(lv) : lv_update_and_reload(lv)))) +- goto err; ++ return_0; + + /* Eliminate any residual LV and don't commit the metadata */ + if (!(r = _eliminate_extracted_lvs_optional_write_vg(lv->vg, removal_lvs, 0))) +- goto err; ++ return_0; + + /* + * Now that any 'REBUILD' or 'RESHAPE_DELTA_DISKS' etc. +@@ -582,25 +612,22 @@ static int _lv_update_reload_fns_reset_eliminate_lvs(struct logical_volume *lv, + */ + log_debug_metadata("Clearing any flags for %s passed to the kernel.", display_lvname(lv)); + if (!(r = _reset_flags_passed_to_kernel(lv, &flags_reset))) +- goto err; ++ return_0; + + /* Call any @fn_post_on_lv before the second update call (e.g. to rename LVs back) */ + if (fn_post_on_lv && !(r = fn_post_on_lv(lv, fn_post_data))) { + log_error("Post callout function failed."); +- goto err; ++ return 0; + } + + /* Update and reload to clear out reset flags in the metadata and in the kernel */ + log_debug_metadata("Updating metadata mappings for %s.", display_lvname(lv)); + if ((r != 2 || flags_reset) && !(r = (origin_only ? lv_update_and_reload_origin(lv) : lv_update_and_reload(lv)))) { + log_error(INTERNAL_ERROR "Update of LV %s failed.", display_lvname(lv)); +- goto err; ++ return 0; + } + +- r = 1; +-err: +- va_end(ap); +- return r; ++ return 1; + } + + /* +@@ -622,6 +649,12 @@ static int _lv_update_and_reload_list(struct logical_volume *lv, int origin_only + struct lv_list *lvl; + int r; + ++ if (lock_lv != lv) { ++ log_debug_activation("Dropping origin_only for %s as lock holds %s", ++ display_lvname(lv), display_lvname(lock_lv)); ++ origin_only = 0; ++ } ++ + log_very_verbose("Updating logical volume %s on disk(s)%s.", + display_lvname(lock_lv), origin_only ? " (origin only)": ""); + +@@ -879,7 +912,7 @@ static int _reorder_raid10_near_seg_areas(struct lv_segment *seg, enum raid0_rai + break; + + default: +- return 0; ++ return_0; + } + + /* Sort areas */ +@@ -1558,7 +1591,7 @@ static int _lv_alloc_reshape_space(struct logical_volume *lv, + lv->size = lv_size_cur; + /* pay attention to lv_extend maybe having allocated more because of layout specific rounding */ + if (!_lv_set_reshape_len(lv, _lv_total_rimage_len(lv) - prev_rimage_len)) +- return 0; ++ return_0; + } + + /* Preset data offset in case we fail relocating reshape space below */ +@@ -1635,7 +1668,7 @@ static int _lv_free_reshape_space_with_status(struct logical_volume *lv, enum al + seg->extents_copied = first_seg(lv)->area_len; + + if (!_lv_set_reshape_len(lv, 0)) +- return 0; ++ return_0; + + /* + * Only in case reshape space was freed at the beginning, +@@ -1689,7 +1722,7 @@ static int _reshaped_state(struct logical_volume *lv, const unsigned dev_count, + return_0; + + if (!_get_dev_health(lv, &kernel_devs, devs_health, devs_in_sync, NULL)) +- return 0; ++ return_0; + + if (kernel_devs == dev_count) + return 1; +@@ -1740,7 +1773,7 @@ static int _reshape_adjust_to_size(struct logical_volume *lv, + uint32_t new_le_count; + + if (!_lv_reshape_get_new_len(lv, old_image_count, new_image_count, &new_le_count)) +- return 0; ++ return_0; + + /* Externally visible LV size w/o reshape space */ + lv->le_count = seg->len = new_le_count; +@@ -1803,7 +1836,7 @@ static int _raid_reshape_add_images(struct logical_volume *lv, + } + + if (!_lv_reshape_get_new_len(lv, old_image_count, new_image_count, &grown_le_count)) +- return 0; ++ return_0; + + current_le_count = lv->le_count - _reshape_len_per_lv(lv); + grown_le_count -= _reshape_len_per_dev(seg) * _data_rimages_count(seg, new_image_count); +@@ -1828,7 +1861,7 @@ static int _raid_reshape_add_images(struct logical_volume *lv, + new_image_count - old_image_count, new_image_count - old_image_count > 1 ? "s" : "", + display_lvname(lv)); + if (!_lv_raid_change_image_count(lv, 1, new_image_count, allocate_pvs, NULL, 0, 0)) +- return 0; ++ return_0; + + /* Reshape adding image component pairs -> change sizes/counters accordingly */ + if (!_reshape_adjust_to_size(lv, old_image_count, new_image_count)) { +@@ -1839,7 +1872,7 @@ static int _raid_reshape_add_images(struct logical_volume *lv, + /* Allocate forward out of place reshape space at the beginning of all data image LVs */ + log_debug_metadata("(Re)allocating reshape space for %s.", display_lvname(lv)); + if (!_lv_alloc_reshape_space(lv, alloc_begin, NULL, allocate_pvs)) +- return 0; ++ return_0; + + /* + * Reshape adding image component pairs: +@@ -1914,7 +1947,7 @@ static int _raid_reshape_remove_images(struct logical_volume *lv, + } + + if (!_lv_reshape_get_new_len(lv, old_image_count, new_image_count, &reduced_le_count)) +- return 0; ++ return_0; + + reduced_le_count -= seg->reshape_len * _data_rimages_count(seg, new_image_count); + current_le_count = lv->le_count - seg->reshape_len * _data_rimages_count(seg, old_image_count); +@@ -1936,7 +1969,7 @@ static int _raid_reshape_remove_images(struct logical_volume *lv, + new_stripes, display_lvname(lv)); + + if (!force) { +- log_warn("WARNING: Can't remove stripes without --force option."); ++ log_error("Can't remove stripes without --force option."); + return 0; + } + +@@ -1952,7 +1985,7 @@ static int _raid_reshape_remove_images(struct logical_volume *lv, + * to remove disks from a raid set + */ + if (!_lv_alloc_reshape_space(lv, alloc_end, NULL, allocate_pvs)) +- return 0; ++ return_0; + + /* Flag all disks past new images as delta disks minus to kernel */ + for (s = new_image_count; s < old_image_count; s++) +@@ -1998,7 +2031,7 @@ static int _raid_reshape_remove_images(struct logical_volume *lv, + old_image_count - new_image_count, old_image_count - new_image_count > 1 ? "s" : "", + display_lvname(lv)); + if (!_lv_raid_change_image_count(lv, 1, new_image_count, allocate_pvs, removal_lvs, 0, 0)) +- return 0; ++ return_0; + + seg->area_count = new_image_count; + break; +@@ -2069,7 +2102,7 @@ static int _raid_reshape_keep_images(struct logical_volume *lv, + + if (alloc_reshape_space && + !_lv_alloc_reshape_space(lv, where, NULL, allocate_pvs)) +- return 0; ++ return_0; + + seg->segtype = new_segtype; + +@@ -2084,15 +2117,22 @@ static int _vg_write_lv_suspend_commit_backup(struct volume_group *vg, + struct logical_volume *lv, + int origin_only, int do_backup) + { ++ const struct logical_volume *lock_lv = lv_lock_holder(lv); + int r = 1; + ++ if (lock_lv != lv) { ++ log_debug_activation("Dropping origin_only for %s as lock holds %s", ++ display_lvname(lv), display_lvname(lock_lv)); ++ origin_only = 0; ++ } ++ + if (!vg_write(vg)) { + log_error("Write of VG %s failed.", vg->name); + return_0; + } + +- if (lv && !(r = (origin_only ? suspend_lv_origin(vg->cmd, lv_lock_holder(lv)) : +- suspend_lv(vg->cmd, lv_lock_holder(lv))))) { ++ if (lv && !(r = (origin_only ? suspend_lv_origin(vg->cmd, lock_lv) : ++ suspend_lv(vg->cmd, lock_lv)))) { + log_error("Failed to suspend %s before committing changes.", + display_lvname(lv)); + vg_revert(lv->vg); +@@ -2133,24 +2173,6 @@ static int _activate_sub_lv_excl_local(struct logical_volume *lv) + return 1; + } + +-/* Helper: function to activate any sub LVs of @lv exclusively local starting with area indexed by @start_idx */ +-static int _activate_sub_lvs_excl_local(struct logical_volume *lv, uint32_t start_idx) +-{ +- uint32_t s; +- struct lv_segment *seg = first_seg(lv); +- +- /* seg->area_count may be 0 here! */ +- log_debug_metadata("Activating %u image component%s of LV %s.", +- seg->area_count - start_idx, seg->meta_areas ? " pairs" : "s", +- display_lvname(lv)); +- for (s = start_idx; s < seg->area_count; s++) +- if (!_activate_sub_lv_excl_local(seg_lv(seg, s)) || +- (seg->meta_areas && !_activate_sub_lv_excl_local(seg_metalv(seg, s)))) +- return_0; +- +- return 1; +-} +- + /* Helper: function to activate any LVs on @lv_list */ + static int _activate_sub_lvs_excl_local_list(struct logical_volume *lv, struct dm_list *lv_list) + { +@@ -2169,20 +2191,6 @@ static int _activate_sub_lvs_excl_local_list(struct logical_volume *lv, struct d + return r; + } + +-/* Helper: callback function to activate image component pairs of @lv to update size after reshape space allocation */ +-static int _pre_raid_reactivate_legs(struct logical_volume *lv, void *data) +-{ +- if (!_vg_write_lv_suspend_vg_commit(lv, 1)) +- return 0; +- +- /* Reload any changed image component pairs for out-of-place reshape space */ +- if (!_activate_sub_lvs_excl_local(lv, 0)) +- return 0; +- +- /* 1: ok+ask caller to update, 2: metadata commited+ask caller to resume */ +- return 2; +-} +- + /* Helper: callback function to activate any rmetas on @data list */ + __attribute__ ((__unused__)) + static int _pre_raid0_remove_rmeta(struct logical_volume *lv, void *data) +@@ -2190,26 +2198,12 @@ static int _pre_raid0_remove_rmeta(struct logical_volume *lv, void *data) + struct dm_list *lv_list = data; + + if (!_vg_write_lv_suspend_vg_commit(lv, 1)) +- return 0; ++ return_0; + + /* 1: ok+ask caller to update, 2: metadata commited+ask caller to resume */ + return _activate_sub_lvs_excl_local_list(lv, lv_list) ? 2 : 0; + } + +-/* Helper: callback dummy needed for takeover+reshape */ +-static int _post_raid_reshape(struct logical_volume *lv, void *data) +-{ +- /* 1: ask caller to update, 2: don't ask caller to update */ +- return 1; +-} +- +-/* Helper: callback dummy needed for takeover+reshape */ +-static int _post_raid_takeover(struct logical_volume *lv, void *data) +-{ +- /* 1: ask caller to update, 2: don't ask caller to update */ +- return 2; +-} +- + /* + * Reshape logical volume @lv by adding/removing stripes + * (absolute new stripes given in @new_stripes), changing +@@ -2251,7 +2245,7 @@ static int _raid_reshape(struct logical_volume *lv, + return_0; + + if (!_check_region_size_constraints(lv, new_segtype, new_region_size, new_stripe_size)) +- return 0; ++ return_0; + + if (!_raid_in_sync(lv)) { + log_error("Unable to convert %s while it is not in-sync.", +@@ -2346,30 +2340,18 @@ static int _raid_reshape(struct logical_volume *lv, + + /* Handle disk addition reshaping */ + if (old_image_count < new_image_count) { +- /* FIXME: remove once MD kernel rhbz1443999 got fixed. */ +- if (sysconf(_SC_NPROCESSORS_ONLN) < 2) { +- log_error("Can't add stripes to LV %s on single core.", display_lvname(lv)); +- return 0; +- } +- + if (!_raid_reshape_add_images(lv, new_segtype, yes, + old_image_count, new_image_count, + new_stripes, new_stripe_size, allocate_pvs)) +- return 0; ++ return_0; + + /* Handle disk removal reshaping */ + } else if (old_image_count > new_image_count) { +- /* FIXME: remove once MD kernel rhbz1443999 got fixed. */ +- if (sysconf(_SC_NPROCESSORS_ONLN) < 2) { +- log_error("Can't remove stripes from LV %s on single core.", display_lvname(lv)); +- return 0; +- } +- + 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; ++ return_0; + + /* + * Handle raid set layout reshaping w/o changing # of legs (allocation algorithm or stripe size change) +@@ -2377,20 +2359,22 @@ static int _raid_reshape(struct logical_volume *lv, + */ + } else if (!_raid_reshape_keep_images(lv, new_segtype, yes, force, &force_repair, + new_data_copies, new_stripe_size, allocate_pvs)) +- return 0; ++ return_0; + + /* HM FIXME: workaround for not resetting "nosync" flag */ + init_mirror_in_sync(0); + + seg->region_size = new_region_size; + ++ /* https://bugzilla.redhat.com/1447812 also check open count */ ++ if (!_check_lv_open_count(lv, 1)) ++ return_0; ++ + 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_reshape, NULL, +- _pre_raid_reactivate_legs, NULL)) +- return 0; ++ if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, &removal_lvs, NULL)) ++ return_0; + } if (!_vg_write_commit_backup(lv->vg)) +- return 0; ++ return_0; + + return 1; + /* FIXME force_repair ? _lv_cond_repair(lv) : 1; */ +@@ -2565,14 +2549,6 @@ static int _raid_add_images_without_commit(struct logical_volume *lv, + return 0; + } + +- if (lv_is_active(lv_lock_holder(lv)) && +- (old_count == 1) && +- (lv_is_thin_pool_data(lv) || lv_is_thin_pool_metadata(lv))) { +- log_error("Can't add image to active thin pool LV %s yet. Deactivate first.", +- display_lvname(lv)); +- return 0; +- } +- + if (!archive(lv->vg)) + return_0; + +@@ -3233,11 +3209,13 @@ int lv_raid_split(struct logical_volume *lv, int yes, const char *split_name, + } + + /* Split on a 2-legged raid1 LV causes losing all resilience */ +- if (new_count == 1 && +- !yes && yes_no_prompt("Are you sure you want to split %s LV %s losing all resilience? [y/n]: ", +- lvseg_name(first_seg(lv)), display_lvname(lv)) == 'n') { +- log_error("Logical volume %s NOT split.", display_lvname(lv)); +- return 0; ++ if (new_count == 1) { ++ if (!yes && yes_no_prompt("Are you sure you want to split %s LV %s losing all resilience? [y/n]: ", ++ lvseg_name(first_seg(lv)), display_lvname(lv)) == 'n') { ++ log_error("Logical volume %s NOT split.", display_lvname(lv)); ++ return 0; ++ } ++ log_verbose("Losing all resilience for logical volume %s.", display_lvname(lv)); + } + + /* +@@ -3375,11 +3353,14 @@ int lv_raid_split_and_track(struct logical_volume *lv, + } + + /* Split and track changes on a 2-legged raid1 LV causes losing resilience for newly written data. */ +- if (seg->area_count == 2 && +- !yes && yes_no_prompt("Are you sure you want to split and track %s LV %s losing resilience for any newly written data? [y/n]: ", +- lvseg_name(seg), display_lvname(lv)) == 'n') { +- log_error("Logical volume %s NOT split.", display_lvname(lv)); +- return 0; ++ if (seg->area_count == 2) { ++ if (!yes && yes_no_prompt("Are you sure you want to split and track %s LV %s losing resilience for any newly written data? [y/n]: ", ++ lvseg_name(seg), display_lvname(lv)) == 'n') { ++ log_error("Logical volume %s NOT split.", display_lvname(lv)); ++ return 0; ++ } ++ log_verbose("Losing resilience for newly written data on logical volume %s.", ++ display_lvname(lv)); + } + + for (s = seg->area_count - 1; s >= 0; --s) { +@@ -3408,7 +3389,7 @@ int lv_raid_split_and_track(struct logical_volume *lv, + return_0; + + if (seg->area_count == 2) +- log_warn("Any newly written data will be non-resilient on LV %s during the split!", ++ log_warn("WARNING: Any newly written data will be non-resilient on LV %s during the split!", + display_lvname(lv)); + + log_print_unless_silent("Use 'lvconvert --merge %s' to merge back into %s.", +@@ -3568,7 +3549,7 @@ static int _add_image_component_list(struct lv_segment *seg, int delete_from_lis + if (delete_from_list) + dm_list_del(&lvl->list); + if (!_add_component_lv(seg, lvl->lv, lv_flags, s++)) +- return 0; ++ return_0; + } + + return 1; +@@ -3674,7 +3655,7 @@ static int _extract_image_component_sublist(struct lv_segment *seg, + + for (s = idx; s < end; s++) { + if (!_extract_image_component_error_seg(seg, type, s, &lvl->lv, error_seg)) +- return 0; ++ return_0; + + dm_list_add(removal_lvs, &lvl->list); + lvl++; +@@ -3831,7 +3812,7 @@ static int _raid0_add_or_remove_metadata_lvs(struct logical_volume *lv, + new_raid_type_flag = SEG_RAID0; + } else { + if (!_alloc_and_add_rmeta_devs_for_lv(lv, allocate_pvs)) +- return 0; ++ return_0; + + new_raid_type_flag = SEG_RAID0_META; + } +@@ -4002,7 +3983,6 @@ static int _convert_raid1_to_mirror(struct logical_volume *lv, + uint32_t new_image_count, + uint32_t new_region_size, + struct dm_list *allocate_pvs, +- int update_and_reload, + struct dm_list *removal_lvs) + { + struct logical_volume *log_lv; +@@ -4042,14 +4022,14 @@ static int _convert_raid1_to_mirror(struct logical_volume *lv, + /* Remove rmeta LVs */ + log_debug_metadata("Extracting and renaming metadata LVs."); + if (!_extract_image_component_list(seg, RAID_META, 0, removal_lvs)) +- return 0; ++ return_0; + + seg->meta_areas = NULL; + + /* Rename all data sub LVs from "*_rimage_*" to "*_mimage_*" and set their status */ + log_debug_metadata("Adjust data LVs of %s.", display_lvname(lv)); + if (!_adjust_data_lvs(lv, RAID1_TO_MIRROR)) +- return 0; ++ return_0; + + seg->segtype = new_segtype; + seg->region_size = new_region_size; +@@ -4059,7 +4039,10 @@ static int _convert_raid1_to_mirror(struct logical_volume *lv, + if (!attach_mirror_log(first_seg(lv), log_lv)) + return_0; + +- return update_and_reload ? _lv_update_reload_fns_reset_eliminate_lvs(lv, 0, removal_lvs, NULL) : 1; ++ if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, removal_lvs, NULL)) ++ return_0; ++ ++ return 1; + } + + /* +@@ -4543,7 +4526,7 @@ static int _process_type_flags(const struct logical_volume *lv, struct possible_ + !(t & seg->segtype->flags) && + ((segtype = get_segtype_from_flag(lv->vg->cmd, t)))) + if (!tfn(processed_segtypes, data ? : (void *) segtype)) +- return 0; ++ return_0; + } + + return 1; +@@ -4794,7 +4777,7 @@ static int _raid1_to_mirrored_wrapper(TAKEOVER_FN_ARGS) + return_0; + + return _convert_raid1_to_mirror(lv, new_segtype, new_image_count, new_region_size, +- allocate_pvs, 1, &removal_lvs); ++ allocate_pvs, &removal_lvs); + } + + /* +@@ -4874,7 +4857,7 @@ static int _clear_meta_lvs(struct logical_volume *lv) + 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_0; + } + + return 1; +@@ -5028,7 +5011,7 @@ static int _raid45_to_raid54_wrapper(TAKEOVER_FN_ARGS) + + /* Shift parity SubLV pair "PDD..." <-> "DD...P" on raid4 <-> raid5_n conversion */ + if( !_shift_parity_dev(seg)) +- return 0; ++ return_0; + + /* Don't resync */ + init_mirror_in_sync(1); +@@ -5068,7 +5051,7 @@ static int _takeover_downconvert_wrapper(TAKEOVER_FN_ARGS) + } + + if (!_check_region_size_constraints(lv, new_segtype, new_region_size, new_stripe_size)) +- return 0; ++ return_0; + + if (seg_is_any_raid10(seg) && (seg->area_count % seg->data_copies)) { + log_error("Can't convert %s LV %s to %s with odd number of stripes.", +@@ -5113,12 +5096,12 @@ static int _takeover_downconvert_wrapper(TAKEOVER_FN_ARGS) + 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; ++ return_0; + + } else if (seg_is_raid10_near(seg)) { + log_debug_metadata("Reordering areas for raid10 -> raid0 takeover."); + if (!_reorder_raid10_near_seg_areas(seg, reorder_from_raid10_near)) +- return 0; ++ return_0; + } + + if (segtype_is_any_raid0(new_segtype) && +@@ -5133,7 +5116,7 @@ static int _takeover_downconvert_wrapper(TAKEOVER_FN_ARGS) + lv_raid_image_count(lv) - new_image_count, + display_lvname(lv)); + if (!_lv_raid_change_image_count(lv, 1, new_image_count, allocate_pvs, &removal_lvs, 0, 0)) +- return 0; ++ return_0; + + seg->area_count = new_image_count; + } +@@ -5283,7 +5266,7 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + } + + if (!_check_region_size_constraints(lv, new_segtype, new_region_size, new_stripe_size)) +- return 0; ++ return_0; + + /* Archive metadata */ + if (!archive(lv->vg)) +@@ -5304,7 +5287,7 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + if (seg_is_raid0(seg)) { + log_debug_metadata("Adding metadata LVs to %s.", display_lvname(lv)); + if (!_raid0_add_or_remove_metadata_lvs(lv, 0 /* update_and_reload */, allocate_pvs, NULL)) +- return 0; ++ return_0; + } + + /* Have to be cleared in conversion from raid0_meta -> raid4 or kernel will reject due to reordering disks */ +@@ -5354,7 +5337,7 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + if (!_eliminate_extracted_lvs(lv->vg, &removal_lvs)) /* Updates vg */ + return_0; + +- return 0; ++ return_0; + } + + seg = first_seg(lv); +@@ -5384,11 +5367,11 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + + if (!_raid45_to_raid54_wrapper(lv, raid5_n_segtype, 1 /* yes */, force, seg->area_count, + 1 /* data_copies */, 0, 0, 0, allocate_pvs)) +- return 0; ++ return_0; + + if (!_drop_suffix(meta_lv->name, "_extracted") || + !_drop_suffix(data_lv->name, "_extracted")) +- return 0; ++ return_0; + + data_lv->status |= RAID_IMAGE; + meta_lv->status |= RAID_META; +@@ -5403,7 +5386,7 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + } else if (segtype_is_raid5_n(new_segtype) && + !_raid45_to_raid54_wrapper(lv, raid5_n_segtype, yes, force, seg->area_count, + 1 /* data_copies */, 0, 0, 0, allocate_pvs)) +- return 0; ++ return_0; + } + + seg->data_copies = new_data_copies; +@@ -5419,7 +5402,7 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + + log_debug_metadata("Reordering areas for raid0 -> raid10_near takeover."); + if (!_reorder_raid10_near_seg_areas(seg, reorder_to_raid10_near)) +- return 0; ++ return_0; + /* Set rebuild flags accordingly */ + for (s = 0; s < seg->area_count; s++) { + seg_lv(seg, s)->status &= ~LV_REBUILD; +@@ -5436,10 +5419,8 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + + log_debug_metadata("Updating VG metadata and reloading %s LV %s.", + lvseg_name(seg), display_lvname(lv)); +- if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, &removal_lvs, +- _post_raid_takeover, NULL, +- _pre_raid_reactivate_legs, NULL)) +- return 0; ++ if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, &removal_lvs, NULL)) ++ return_0; + + if (segtype_is_raid4(new_segtype) && + seg->area_count != 2) { +@@ -5975,7 +5956,7 @@ static int _set_convenient_raid145610_segtype_to(const struct lv_segment *seg_fr + /* raid1 -> */ + } else if (seg_is_raid1(seg_from) && !segtype_is_mirror(*segtype)) { + if (seg_from->area_count != 2) { +- log_warn("Convert %s LV %s to 2 images first.", ++ log_error("Convert %s LV %s to 2 images first.", + lvseg_name(seg_from), display_lvname(seg_from->lv)); + return 0; + +@@ -5991,8 +5972,8 @@ static int _set_convenient_raid145610_segtype_to(const struct lv_segment *seg_fr + } else if (seg_is_raid4(seg_from) || seg_is_any_raid5(seg_from)) { + if (segtype_is_raid1(*segtype) && + seg_from->area_count != 2) { +- log_warn("Convert %s LV %s to 2 stripes first (i.e. --stripes 1).", +- lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ log_error("Convert %s LV %s to 2 stripes first (i.e. --stripes 1).", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); + return 0; + + } else if (seg_is_raid4(seg_from) && +@@ -6007,8 +5988,8 @@ static int _set_convenient_raid145610_segtype_to(const struct lv_segment *seg_fr + + else if (segtype_is_raid10(*segtype)) { + if (seg_from->area_count < 3) { +- log_warn("Convert %s LV %s to minimum 3 stripes first (i.e. --stripes 2).", +- lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ log_error("Convert %s LV %s to minimum 3 stripes first (i.e. --stripes 2).", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); + return 0; + } + +@@ -6016,8 +5997,8 @@ static int _set_convenient_raid145610_segtype_to(const struct lv_segment *seg_fr + + } else if (segtype_is_any_raid6(*segtype)) { + if (seg_from->area_count < 4) { +- log_warn("Convert %s LV %s to minimum 4 stripes first (i.e. --stripes 3).", +- lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ log_error("Convert %s LV %s to minimum 4 stripes first (i.e. --stripes 3).", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); + return 0; + + } else if (seg_is_raid4(seg_from) && !segtype_is_raid6_n_6(*segtype)) +@@ -6053,12 +6034,12 @@ static int _set_convenient_raid145610_segtype_to(const struct lv_segment *seg_fr + /* -> raid1 */ + } else if (!seg_is_mirror(seg_from) && segtype_is_raid1(*segtype)) { + if (!seg_is_raid4(seg_from) && !seg_is_any_raid5(seg_from)) { +- log_warn("Convert %s LV %s to raid4/raid5 first.", +- lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ log_error("Convert %s LV %s to raid4/raid5 first.", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); + return 0; + + } else if (seg_from->area_count != 2) { +- log_warn("Convert %s LV %s to 2 stripes first (i.e. --stripes 1).", ++ log_error("Convert %s LV %s to 2 stripes first (i.e. --stripes 1).", + lvseg_name(seg_from), display_lvname(seg_from->lv)); + return 0; + +@@ -6113,7 +6094,7 @@ static int _region_size_change_requested(struct logical_volume *lv, int yes, con + } + + if (!_check_region_size_constraints(lv, seg->segtype, region_size, seg->stripe_size)) +- return 0; ++ return_0; + + if (!_raid_in_sync(lv)) { + log_error("Unable to change region size on %s LV %s while it is not in-sync.", +@@ -6224,6 +6205,27 @@ static int _conversion_options_allowed(const struct lv_segment *seg_from, + return r; + } + ++/* https://bugzilla.redhat.com/1447812 try opening LV exclusively */ ++static int _lv_open_excl(struct logical_volume *lv, struct device **dev) { ++ char *dev_path; ++ size_t sz = strlen(lv->vg->cmd->dev_dir) + strlen(lv->vg->name) + strlen(lv->name) + 2; ++ ++ *dev = NULL; ++ if (!(dev_path = dm_pool_alloc(lv->vg->cmd->mem, sz))) ++ return_0; ++ if (dm_snprintf(dev_path, sz, "%s%s/%s", lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0) ++ return_0; ++ if (!(*dev = dev_create_file(dev_path, NULL, NULL, 0))) ++ return_0; ++ if (!dev_open_flags(*dev, O_EXCL, 1, 1)) { ++ log_error("Reshape is only supported when %s is not in use (e.g. unmount filesystem).", ++ display_lvname(lv)); ++ return 0; ++ } ++ ++ return 1; ++} ++ + /* + * lv_raid_convert + * +@@ -6273,6 +6275,7 @@ int lv_raid_convert(struct logical_volume *lv, + uint32_t region_size; + uint32_t data_copies = seg->data_copies; + uint32_t available_slvs, removed_slvs; ++ struct device *dev; + takeover_fn_t takeover_fn; + + /* FIXME If not active, prompt and activate */ +@@ -6282,6 +6285,12 @@ int lv_raid_convert(struct logical_volume *lv, + log_error("%s must be active to perform this operation.", + display_lvname(lv)); + return 0; ++ } else if (vg_is_clustered(lv->vg) && ++ !lv_is_active_exclusive_locally(lv_lock_holder(lv))) { ++ /* In clustered VGs, the LV must be active on this node exclusively. */ ++ log_error("%s must be active exclusive locally to " ++ "perform this operation.", display_lvname(lv)); ++ return 0; + } + + new_segtype = new_segtype ? : seg->segtype; +@@ -6318,6 +6327,12 @@ int lv_raid_convert(struct logical_volume *lv, + new_stripes, new_stripe_size_supplied)) + return _log_possible_conversion_types(lv, new_segtype); + ++ /* https://bugzilla.redhat.com/1439399 */ ++ if (lv_is_origin(lv)) { ++ log_error("Can't convert snapshot origin %s.", display_lvname(lv)); ++ return 0; ++ } ++ + /* + * reshape of capable raid type requested + */ +@@ -6325,13 +6340,31 @@ int lv_raid_convert(struct logical_volume *lv, + case 0: + break; + case 1: ++ /* Conversion of reshapable raids is the cluster is not supported yet. */ ++ if (locking_is_clustered()) { ++ log_error("Conversion of %s not supported in the cluster.", display_lvname(lv)); ++ return 0; ++ } ++ ++ /* https://bugzilla.redhat.com/1447812 reject reshape on open LV */ ++ if (!_check_lv_open_count(lv, 0)) ++ return_0; ++ if (!_lv_open_excl(lv, &dev)) ++ return_0; ++ if (!_check_lv_open_count(lv, 1)) { ++ dev_close(dev); ++ return_0; ++ } ++ + if (!_raid_reshape(lv, new_segtype, yes, force, + data_copies, region_size, + stripes, stripe_size, allocate_pvs)) { ++ dev_close(dev); + log_error("Reshape request failed on LV %s.", display_lvname(lv)); + return 0; + } + ++ dev_close(dev); + return 1; + case 2: + log_error("Invalid conversion request on %s.", display_lvname(lv)); +@@ -6344,7 +6377,8 @@ int lv_raid_convert(struct logical_volume *lv, + + /* Prohibit any takeover in case sub LVs to be removed still exist after a previous reshape */ + if (!_get_available_removed_sublvs(lv, &available_slvs, &removed_slvs)) +- return 0; ++ return_0; ++ + if (removed_slvs) { + log_error("Can't convert %s LV %s to %s containing sub LVs to remove after a reshape.", + lvseg_name(seg), display_lvname(lv), new_segtype->name); +@@ -6387,13 +6421,6 @@ int lv_raid_convert(struct logical_volume *lv, + (segtype_is_striped_target(new_segtype) && + (new_stripes == 1)) ? SEG_TYPE_NAME_LINEAR : new_segtype->name); + +- /* In clustered VGs, the LV must be active on this node exclusively. */ +- if (vg_is_clustered(lv->vg) && !lv_is_active_exclusive_locally(lv)) { +- log_error("%s must be active exclusive locally to " +- "perform this operation.", display_lvname(lv)); +- return 0; +- } +- + /* LV must be in sync. */ + if (!_raid_in_sync(lv)) { + log_error("Unable to convert %s while it is not in-sync.", +@@ -6634,6 +6661,11 @@ static int _lv_raid_rebuild_or_replace(struct logical_volume *lv, + } + + if (!match_count) { ++ if (remove_pvs && !dm_list_empty(remove_pvs)) { ++ log_error("Logical volume %s does not contain devices specified to %s.", ++ display_lvname(lv), action_str); ++ return 0; ++ } + log_print_unless_silent("%s does not contain devices specified to %s.", + display_lvname(lv), action_str); + return 1; +diff --git a/lib/raid/raid.c b/lib/raid/raid.c +index 8a53d7e..c5cfb0f 100644 +--- a/lib/raid/raid.c ++++ b/lib/raid/raid.c +@@ -358,36 +358,21 @@ static int _raid_target_percent(void **target_state, + uint64_t *total_numerator, + uint64_t *total_denominator) + { +- int i; +- uint64_t numerator, denominator; +- char *pos = params; +- /* +- * Status line: +- * <#devs> / +- * Example: +- * raid1 2 AA 1024000/1024000 +- */ +- for (i = 0; i < 3; i++) { +- pos = strstr(pos, " "); +- if (pos) +- pos++; +- else +- break; +- } +- if (!pos || (sscanf(pos, FMTu64 "/" FMTu64 "%n", &numerator, &denominator, &i) != 2) || +- !denominator) { +- log_error("Failed to parse %s status fraction: %s", +- (seg) ? seg->segtype->name : "segment", params); +- return 0; +- } ++ struct dm_status_raid *sr; ++ ++ if (!dm_get_status_raid(mem, params, &sr)) ++ return_0; + +- *total_numerator += numerator; +- *total_denominator += denominator; ++ *total_numerator += sr->insync_regions; ++ *total_denominator += sr->total_regions; + + if (seg) +- seg->extents_copied = (uint64_t) seg->area_len * dm_make_percent(numerator, denominator) / DM_PERCENT_100; ++ seg->extents_copied = (uint64_t) seg->area_len ++ * dm_make_percent(sr->insync_regions , sr->total_regions) / DM_PERCENT_100; ++ ++ *percent = dm_make_percent(sr->insync_regions, sr->total_regions); + +- *percent = dm_make_percent(numerator, denominator); ++ dm_pool_free(mem, sr); + + return 1; + } +@@ -475,7 +460,7 @@ static int _raid_target_present(struct cmd_context *cmd, + { 1, 7, 0, RAID_FEATURE_RAID0, SEG_TYPE_NAME_RAID0 }, + { 1, 9, 0, RAID_FEATURE_SHRINK, "shrinking" }, + { 1, 9, 0, RAID_FEATURE_NEW_DEVICES_ACCEPT_REBUILD, "rebuild+emptymeta" }, +- { 1, 10, 1, RAID_FEATURE_RESHAPE, "reshaping" }, ++ { 1, 12, 0, RAID_FEATURE_RESHAPE, "reshaping" }, + }; + + static int _raid_checked = 0; +diff --git a/lib/report/report.c b/lib/report/report.c +index d9880b2..f61776e 100644 +--- a/lib/report/report.c ++++ b/lib/report/report.c +@@ -3079,11 +3079,13 @@ static int _copypercent_disp(struct dm_report *rh, + dm_percent_t percent = DM_PERCENT_INVALID; + + /* TODO: just cache passes through lvseg_percent... */ +- if (lv_is_cache(lv) || lv_is_used_cache_pool(lv)) ++ if (lv_is_cache(lv) || lv_is_used_cache_pool(lv) || ++ (!lv_is_merging_origin(lv) && lv_is_raid(lv) && !seg_is_any_raid0(first_seg(lv)))) + percent = lvseg_percent_with_info_and_seg_status(lvdm, PERCENT_GET_DIRTY); +- else if (((lv_is_raid(lv) && !seg_is_any_raid0(first_seg(lv)) && +- lv_raid_percent(lv, &percent)) || +- (lv_is_mirror(lv) && ++ else if (lv_is_raid(lv) && !seg_is_any_raid0(first_seg(lv))) ++ /* old way for percentage when merging snapshot into raid origin */ ++ (void) lv_raid_percent(lv, &percent); ++ else if (((lv_is_mirror(lv) && + lv_mirror_percent(lv->vg->cmd, lv, 0, &percent, NULL))) && + (percent != DM_PERCENT_INVALID)) + percent = copy_percent(lv); +diff --git a/libdm/libdm-targets.c b/libdm/libdm-targets.c +index 1709c2b..6577f07 100644 +--- a/libdm/libdm-targets.c ++++ b/libdm/libdm-targets.c +@@ -99,6 +99,7 @@ int dm_get_status_raid(struct dm_pool *mem, const char *params, + unsigned num_fields; + const char *p, *pp, *msg_fields = ""; + struct dm_status_raid *s = NULL; ++ unsigned a = 0; + + if ((num_fields = _count_fields(params)) < 4) + goto_bad; +@@ -168,6 +169,23 @@ int dm_get_status_raid(struct dm_pool *mem, const char *params, + out: + *status = s; + ++ if (s->insync_regions == s->total_regions) { ++ /* FIXME: kernel gives misleading info here ++ * Trying to recognize a true state */ ++ while (i-- > 0) ++ if (s->dev_health[i] == 'a') ++ a++; /* Count number of 'a' */ ++ ++ if (a && a < s->dev_count) { ++ /* SOME legs are in 'a' */ ++ if (!strcasecmp(s->sync_action, "recover") ++ || !strcasecmp(s->sync_action, "idle")) ++ /* Kernel may possibly start some action ++ * in near-by future, do not report 100% */ ++ s->insync_regions--; ++ } ++ } ++ + return 1; + + bad: +diff --git a/scripts/fsadm.sh b/scripts/fsadm.sh +index ea14efe..adf9b55 100755 +--- a/scripts/fsadm.sh ++++ b/scripts/fsadm.sh +@@ -402,6 +402,7 @@ yes_no() { + while read -r -s -n 1 ANS ; do + case "$ANS" in + "y" | "Y" ) echo y ; return 0 ;; ++ "n" | "N") break ;; + "" ) if [ -t 1 ] ; then + echo y ; return 0 + fi ;; +diff --git a/test/lib/check.sh b/test/lib/check.sh +index 64812fb..5eec936 100644 +--- a/test/lib/check.sh ++++ b/test/lib/check.sh +@@ -411,7 +411,7 @@ raid_leg_status() { + + grep_dmsetup() { + dmsetup $1 $2 | tee out +- grep "${@:3}" out || die "Expected output from dmsetup $1 not found!" ++ grep -q "${@:3}" out || die "Expected output \"${@:3}\" from dmsetup $1 not found!" + } + + #set -x +diff --git a/test/shell/fsadm.sh b/test/shell/fsadm.sh +index ac43900..6eff24a 100644 +--- a/test/shell/fsadm.sh ++++ b/test/shell/fsadm.sh +@@ -131,6 +131,7 @@ if check_missing ext3; then + not fsadm -y --lvresize resize $vg_lv 4M + echo n | not lvresize -L4M -r -n $vg_lv + lvresize -L+20M -r -n $vg_lv ++ lvresize -L-10M -r -y $vg_lv + umount "$mount_dir" + umount "$mount_space_dir" + fscheck_ext3 +diff --git a/test/shell/lvconvert-raid-status-validation.sh b/test/shell/lvconvert-raid-status-validation.sh +new file mode 100644 +index 0000000..d705cc2 +--- /dev/null ++++ b/test/shell/lvconvert-raid-status-validation.sh +@@ -0,0 +1,127 @@ ++####################################################################### ++# This series of tests is meant to validate the correctness of ++# 'dmsetup status' for RAID LVs - especially during various sync action ++# transitions, like: recover, resync, check, repair, idle, reshape, etc ++####################################################################### ++SKIP_WITH_LVMLOCKD=1 ++SKIP_WITH_LVMPOLLD=1 ++ ++export LVM_TEST_LVMETAD_DEBUG_OPTS=${LVM_TEST_LVMETAD_DEBUG_OPTS-} ++ ++. lib/inittest ++ ++# check for version 1.9.0 ++# - it is the point at which linear->raid1 uses "recover" ++aux have_raid 1 9 0 || skip ++ ++aux prepare_pvs 9 ++vgcreate -s 2m $vg $(cat DEVICES) ++ ++########################################### ++# Upconverted RAID1 should never have all 'a's in status output ++########################################### ++aux delay_dev $dev2 0 50 ++lvcreate -aey -l 2 -n $lv1 $vg $dev1 ++lvconvert --type raid1 -y -m 1 $vg/$lv1 $dev2 ++while ! check in_sync $vg $lv1; do ++ a=( $(dmsetup status $vg-$lv1) ) || die "Unable to get status of $vg/$lv1" ++ [ ${a[5]} != "aa" ] ++ sleep .1 ++done ++aux enable_dev $dev2 ++lvremove -ff $vg ++ ++########################################### ++# Upconverted RAID1 should not be at 100% right after upconvert ++########################################### ++aux delay_dev $dev2 0 50 ++lvcreate -aey -l 2 -n $lv1 $vg $dev1 ++lvconvert --type raid1 -y -m 1 $vg/$lv1 $dev2 ++a=( $(dmsetup status $vg-$lv1) ) || die "Unable to get status of $vg/$lv1" ++b=( $(echo ${a[6]} | sed s:/:' ':) ) ++[ ${b[0]} -ne ${b[1]} ] ++aux enable_dev $dev2 ++lvremove -ff $vg ++ ++########################################### ++# Catch anything suspicious with linear -> RAID1 upconvert ++########################################### ++aux delay_dev $dev2 0 50 ++lvcreate -aey -l 2 -n $lv1 $vg $dev1 ++lvconvert --type raid1 -y -m 1 $vg/$lv1 $dev2 ++while true; do ++ a=( $(dmsetup status $vg-$lv1) ) || die "Unable to get status of $vg/$lv1" ++ b=( $(echo ${a[6]} | sed s:/:' ':) ) ++ if [ ${b[0]} -ne ${b[1]} ]; then ++ # If the sync operation ("recover" in this case) is not ++ # finished, then it better be as follows: ++ [ ${a[5]} == "Aa" ] ++ [ ${a[7]} == "recover" ] ++ else ++ # Tough to tell the INVALID case, ++ # Before starting sync thread: "Aa X/X recover" ++ # from the valid case, ++ # Just finished sync thread: "Aa X/X recover" ++ # We'll just put "should" for now ++ should [ ${a[5]} == "AA" ] ++ should [ ${a[7]} == "idle" ] ++ break ++ fi ++ sleep .1 ++done ++aux enable_dev $dev2 ++lvremove -ff $vg ++ ++########################################### ++# Catch anything suspicious with RAID1 2-way -> 3-way upconvert ++########################################### ++aux delay_dev $dev3 0 50 ++lvcreate --type raid1 -m 1 -aey -l 2 -n $lv1 $vg $dev1 $dev2 ++lvconvert -y -m +1 $vg/$lv1 $dev3 ++while true; do ++ a=( $(dmsetup status $vg-$lv1) ) || die "Unable to get status of $vg/$lv1" ++ b=( $(echo ${a[6]} | sed s:/:' ':) ) ++ if [ ${b[0]} -ne ${b[1]} ]; then ++ # If the sync operation ("recover" in this case) is not ++ # finished, then it better be as follows: ++ [ ${a[5]} == "AAa" ] ++ [ ${a[7]} == "recover" ] ++ else ++ # Tough to tell the INVALID case, ++ # Before starting sync thread: "Aa X/X recover" ++ # from the valid case, ++ # Just finished sync thread: "Aa X/X recover" ++ # We'll just put "should" for now ++ should [ ${a[5]} == "AAA" ] ++ should [ ${a[7]} == "idle" ] ++ break ++ fi ++ sleep .1 ++done ++aux enable_dev $dev3 ++lvremove -ff $vg ++ ++########################################### ++# Catch anything suspicious with RAID1 initial resync ++########################################### ++aux delay_dev $dev2 0 50 ++lvcreate --type raid1 -m 1 -aey -l 2 -n $lv1 $vg $dev1 $dev2 ++while true; do ++ a=( $(dmsetup status $vg-$lv1) ) || die "Unable to get status of $vg/$lv1" ++ b=( $(echo ${a[6]} | sed s:/:' ':) ) ++ if [ ${b[0]} -ne ${b[1]} ]; then ++ # If the sync operation ("resync" in this case) is not ++ # finished, then it better be as follows: ++ [ ${a[5]} == "aa" ] ++ [ ${a[7]} == "resync" ] ++ else ++ should [ ${a[5]} == "AA" ] ++ should [ ${a[7]} == "idle" ] ++ break ++ fi ++ sleep .1 ++done ++aux enable_dev $dev2 ++lvremove -ff $vg ++ ++vgremove -ff $vg +diff --git a/test/shell/lvconvert-raid-takeover-alloc-failure.sh b/test/shell/lvconvert-raid-takeover-alloc-failure.sh +index 3870c35..cf926a1 100644 +--- a/test/shell/lvconvert-raid-takeover-alloc-failure.sh ++++ b/test/shell/lvconvert-raid-takeover-alloc-failure.sh +@@ -48,7 +48,7 @@ function check_no_sub_lvs + # Check takover upconversion fails allocation errors nicely without leaving image pair remnants behind + + # 6-way striped: neither conversion to raid5 nor raid6 possible +-lvcreate --yes --stripes 6 --size 4M --name $lv1 $vg ++lvcreate -aey --yes --stripes 6 --size 4M --name $lv1 $vg + not lvconvert --yes --type raid4 $vg/$lv1 + check lv_field $vg/$lv1 segtype "striped" + check_no_sub_lvs $vg $lv1 0 5 +@@ -69,7 +69,7 @@ check_sub_lvs $vg $lv1 0 5 + lvremove -y $vg + + # 5-way striped: conversion to raid5 possible but not to raid6 +-lvcreate --yes --stripes 5 --size 4M --name $lv1 $vg ++lvcreate -aey --stripes 5 --size 4M --name $lv1 $vg + not lvconvert --yes --type raid6 $vg/$lv1 + check lv_field $vg/$lv1 segtype "striped" + check_no_sub_lvs $vg $lv1 0 5 +@@ -83,7 +83,7 @@ check_sub_lvs $vg $lv1 0 5 + lvremove -y $vg + + # 4-way striped: conversion to raid5 and raid6 possible +-lvcreate --yes --stripes 4 --size 4M --name $lv1 $vg ++lvcreate -aey --stripes 4 --size 4M --name $lv1 $vg + lvconvert --yes --type raid5 $vg/$lv1 + check lv_field $vg/$lv1 segtype "raid5_n" + check lv_field $vg/$lv1 stripes 5 +@@ -92,7 +92,8 @@ check_sub_lvs $vg $lv1 0 4 + check_no_sub_lvs $vg $lv1 5 5 + + lvremove -y $vg +-lvcreate --yes --stripes 4 --size 4M --name $lv1 $vg ++ ++lvcreate -aey --stripes 4 --size 4M --name $lv1 $vg + lvconvert --yes --type raid6 $vg/$lv1 + check lv_field $vg/$lv1 segtype "raid6_n_6" + check lv_field $vg/$lv1 stripes 6 +diff --git a/test/shell/lvconvert-raid-takeover-thin.sh b/test/shell/lvconvert-raid-takeover-thin.sh +new file mode 100644 +index 0000000..647b133 +--- /dev/null ++++ b/test/shell/lvconvert-raid-takeover-thin.sh +@@ -0,0 +1,72 @@ ++#!/bin/sh ++# Copyright (C) 2017 Red Hat, Inc. All rights reserved. ++# ++# This copyrighted material is made available to anyone wishing to use, ++# modify, copy, or redistribute it subject to the terms and conditions ++# of the GNU General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 51 Franklin Street, Fifth Floor, Boston, MA2110-1301 USA ++ ++# check we may convert thin-pool to raid1/raid10 and back ++# RHBZ#1365286 ++ ++SKIP_WITH_LVMLOCKD=1 ++SKIP_WITH_LVMPOLLD=1 ++ ++. lib/inittest ++ ++aux have_thin 1 0 0 || skip ++aux have_raid 1 9 0 || skip ++ ++aux prepare_vg 6 ++ ++lvcreate -L4 -i3 -T $vg/pool -V10 ++ ++for i in 1 2 ; do ++lvconvert --type raid10 -y $vg/pool_tdata ++check grep_dmsetup table $vg-pool_tdata "raid10" ++aux wait_for_sync $vg pool_tdata ++ ++lvconvert --type striped -y $vg/pool_tdata ++check grep_dmsetup table $vg-pool_tdata "striped" ++done ++ ++lvremove -f $vg ++ ++lvcreate -L4 -T $vg/pool -V10 -n $lv1 ++ ++for j in data meta ; do ++ LV=pool_t${j} ++ for i in 1 2 ; do ++ lvconvert --type raid1 -m1 -y $vg/$LV ++ check grep_dmsetup table $vg-${LV} "raid1" ++ aux wait_for_sync $vg $LV ++ ++ lvconvert --type raid1 -m0 -y $vg/$LV ++ check grep_dmsetup table ${vg}-${LV} "linear" ++ done ++done ++ ++ ++# ++# Now same test again, when lock holding LV is not a thin-poll ++# but thinLV $lv1 ++# ++lvchange -an $vg ++lvchange -ay $vg/$lv1 ++ ++for j in data meta ; do ++ LV=pool_t${j} ++ for i in 1 2 ; do ++ lvconvert --type raid1 -m1 -y $vg/$LV ++ check grep_dmsetup table $vg-${LV} "raid1" ++ aux wait_for_sync $vg $LV ++ ++ lvconvert --type raid1 -m0 -y $vg/$LV ++ check grep_dmsetup table ${vg}-${LV} "linear" ++ done ++done ++ ++vgremove -ff $vg +diff --git a/tools/lvresize.c b/tools/lvresize.c +index 1d59239..9b061ac 100644 +--- a/tools/lvresize.c ++++ b/tools/lvresize.c +@@ -147,6 +147,7 @@ static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv, + lp->argv = ++argv; + + lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, 0); ++ lp->yes = arg_is_set(cmd, yes_ARG); + lp->force = arg_is_set(cmd, force_ARG); + lp->nofsck = arg_is_set(cmd, nofsck_ARG); + lp->nosync = arg_is_set(cmd, nosync_ARG); diff --git a/SOURCES/lvm2-2_02_172-upstream.patch b/SOURCES/lvm2-2_02_172-upstream.patch new file mode 100644 index 0000000..ea903d7 --- /dev/null +++ b/SOURCES/lvm2-2_02_172-upstream.patch @@ -0,0 +1,4536 @@ + WHATS_NEW | 19 + + WHATS_NEW_DM | 5 + + daemons/dmeventd/plugins/raid/dmeventd_raid.c | 16 + + daemons/dmeventd/plugins/thin/dmeventd_thin.c | 12 +- + daemons/dmfilemapd/dmfilemapd.c | 49 +- + daemons/lvmdbusd/automatedproperties.py | 2 +- + daemons/lvmdbusd/background.py | 13 +- + daemons/lvmdbusd/cmdhandler.py | 18 +- + daemons/lvmdbusd/fetch.py | 10 +- + daemons/lvmdbusd/objectmanager.py | 10 +- + daemons/lvmdbusd/utils.py | 4 +- + lib/activate/activate.c | 1 - + lib/config/config_settings.h | 7 +- + lib/config/defaults.h | 4 +- + lib/format_text/export.c | 32 +- + lib/format_text/flags.c | 107 +++- + lib/format_text/import-export.h | 17 +- + lib/format_text/import_vsn1.c | 23 +- + lib/metadata/merge.c | 2 +- + lib/metadata/metadata-exported.h | 5 +- + lib/metadata/metadata.c | 129 ++++- + lib/metadata/raid_manip.c | 598 ++++++++++++++++----- + lib/metadata/segtype.h | 18 + + lib/metadata/thin_manip.c | 19 +- + lib/raid/raid.c | 1 + + libdm/libdm-stats.c | 24 +- + liblvm/lvm_lv.c | 2 +- + man/clvmd.8_main | 7 +- + man/lvm-fullreport.8_des | 1 - + man/lvm.8_main | 2 + + man/lvm.conf.5_main | 4 + + man/lvmcache.7_main | 18 + + man/lvmetad.8_main | 11 +- + man/lvmlockd.8_main | 97 ++-- + man/lvmraid.7_main | 132 +++-- + man/lvmreport.7_main | 2 +- + man/lvmsystemid.7_main | 19 +- + man/pvchange.8_des | 3 + + man/see_also.end | 2 + + man/vgexport.8_des | 2 +- + scripts/fsadm.sh | 203 +++++-- + test/dbus/lvmdbustest.py | 56 +- + test/shell/fsadm-renamed.sh | 116 ++++ + test/shell/lvchange-raid.sh | 1 + + test/shell/lvconvert-raid-regionsize.sh | 8 + + .../lvconvert-raid-reshape-striped_to_linear.sh | 2 +- + test/shell/lvconvert-raid.sh | 61 +++ + test/shell/lvconvert-thin.sh | 2 +- + test/shell/lvcreate-cache.sh | 6 + + test/shell/lvcreate-large-raid.sh | 10 +- + test/shell/lvcreate-raid-nosync.sh | 6 +- + test/shell/lvcreate-thin-big.sh | 2 +- + test/shell/lvresize-full.sh | 1 + + test/shell/thin-dmeventd-warns.sh | 85 +++ + test/shell/thin-large.sh | 54 ++ + test/shell/thin-resize-match.sh | 3 +- + test/shell/unknown-segment.sh | 9 +- + test/unit/Makefile.in | 14 +- + tools/args.h | 99 +++- + tools/command-lines.in | 2 +- + tools/command.c | 27 +- + tools/lvchange.c | 10 + + tools/lvcreate.c | 44 +- + tools/reporter.c | 6 +- + tools/toollib.c | 18 +- + 65 files changed, 1829 insertions(+), 453 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 33a27e8..b2796f6 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,3 +1,22 @@ ++Version 2.02.172 - ++=============================== ++ No longer necessary to '--force' a repair for RAID1 ++ Linear to RAID1 upconverts now use "recover" sync action, not "resync". ++ Improve lvcreate --cachepool arg validation. ++ Limit maximal size of thin-pool for specific chunk size. ++ Print a warning about in-use PVs with no VG using them. ++ Disable automatic clearing of PVs that look like in-use orphans. ++ Cache format2 flag is now using segment name type field. ++ Support storing status flags via segtype name field. ++ Stop using '--yes' mode when fsadm runs without terminal. ++ Extend validation of filesystems resized by fsadm. ++ Enhance lvconvert automatic settings of possible (raid) LV types. ++ Allow lvchange to change properties on a thin pool data sub LV. ++ Fix lvcreate extent percentage calculation for mirrors. ++ Don't reinstate still-missing devices when correcting inconsistent metadata. ++ Properly handle subshell return codes in fsadm. ++ Disallow cachepool creation with policy cleaner and mode writeback. ++ + Version 2.02.171 - 3rd May 2017 + =============================== + Fix memory warnings by using mempools for command definition processing. +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index 20d6220..5718ab7 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,3 +1,8 @@ ++Version 1.02.141 - ++=============================== ++ Accept truncated files in calls to dm_stats_update_regions_from_fd(). ++ Restore Warning by 5% increment when thin-pool is over 80% (1.02.138). ++ + Version 1.02.140 - 3rd May 2017 + =============================== + Add missing configure --enable-dmfilemapd status message and fix --disable. +diff --git a/daemons/dmeventd/plugins/raid/dmeventd_raid.c b/daemons/dmeventd/plugins/raid/dmeventd_raid.c +index 4f204bf..afeac28 100644 +--- a/daemons/dmeventd/plugins/raid/dmeventd_raid.c ++++ b/daemons/dmeventd/plugins/raid/dmeventd_raid.c +@@ -58,6 +58,22 @@ static int _process_raid_event(struct dso_state *state, char *params, const char + dead = 1; + } + ++ /* ++ * if we are converting from non-RAID to RAID (e.g. linear -> raid1) ++ * and too many original devices die, such that we cannot continue ++ * the "recover" operation, the sync action will go to "idle", the ++ * unsynced devs will remain at 'a', and the original devices will ++ * NOT SWITCH TO 'D', but will remain at 'A' - hoping to be revived. ++ * ++ * This is simply the way the kernel works... ++ */ ++ if (!strcmp(status->sync_action, "idle") && ++ strchr(status->dev_health, 'a')) { ++ log_error("Primary sources for new RAID, %s, have failed.", ++ device); ++ dead = 1; /* run it through LVM repair */ ++ } ++ + if (dead) { + if (status->insync_regions < status->total_regions) { + if (!state->warned) { +diff --git a/daemons/dmeventd/plugins/thin/dmeventd_thin.c b/daemons/dmeventd/plugins/thin/dmeventd_thin.c +index e7d24c5..7fd7b0e 100644 +--- a/daemons/dmeventd/plugins/thin/dmeventd_thin.c ++++ b/daemons/dmeventd/plugins/thin/dmeventd_thin.c +@@ -47,10 +47,8 @@ struct dso_state { + struct dm_pool *mem; + int metadata_percent_check; + int metadata_percent; +- int metadata_warn_once; + int data_percent_check; + int data_percent; +- int data_warn_once; + uint64_t known_metadata_size; + uint64_t known_data_size; + unsigned fails; +@@ -253,9 +251,8 @@ void process_event(struct dm_task *dmt, + * action is called for: >50%, >55% ... >95%, 100% + */ + state->metadata_percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks); +- if (state->metadata_percent <= WARNING_THRESH) +- state->metadata_warn_once = 0; /* Dropped bellow threshold, reset warn once */ +- else if (!state->metadata_warn_once++) /* Warn once when raised above threshold */ ++ if ((state->metadata_percent > WARNING_THRESH) && ++ (state->metadata_percent > state->metadata_percent_check)) + log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.", + device, dm_percent_to_float(state->metadata_percent)); + if (state->metadata_percent > CHECK_MINIMUM) { +@@ -269,9 +266,8 @@ void process_event(struct dm_task *dmt, + state->metadata_percent_check = CHECK_MINIMUM; + + state->data_percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks); +- if (state->data_percent <= WARNING_THRESH) +- state->data_warn_once = 0; +- else if (!state->data_warn_once++) ++ if ((state->data_percent > WARNING_THRESH) && ++ (state->data_percent > state->data_percent_check)) + log_warn("WARNING: Thin pool %s data is now %.2f%% full.", + device, dm_percent_to_float(state->data_percent)); + if (state->data_percent > CHECK_MINIMUM) { +diff --git a/daemons/dmfilemapd/dmfilemapd.c b/daemons/dmfilemapd/dmfilemapd.c +index 2dd069a..43ae970 100644 +--- a/daemons/dmfilemapd/dmfilemapd.c ++++ b/daemons/dmfilemapd/dmfilemapd.c +@@ -266,8 +266,6 @@ static int _parse_args(int argc, char **argv, struct filemap_monitor *fm) + return 0; + } + +- memset(fm, 0, sizeof(*fm)); +- + /* + * We don't know the true nr_regions at daemon start time, + * and it is not worth a dm_stats_list()/group walk to count: +@@ -359,30 +357,33 @@ static int _parse_args(int argc, char **argv, struct filemap_monitor *fm) + return 1; + } + +-static int _filemap_fd_check_changed(struct filemap_monitor *fm) ++static int _filemap_fd_update_blocks(struct filemap_monitor *fm) + { +- int64_t blocks, old_blocks; + struct stat buf; + + if (fm->fd < 0) { + log_error("Filemap fd is not open."); +- return -1; ++ return 0; + } + + if (fstat(fm->fd, &buf)) { + log_error("Failed to fstat filemap file descriptor."); +- return -1; ++ return 0; + } + +- blocks = buf.st_blocks; ++ fm->blocks = buf.st_blocks; + +- /* first check? */ +- if (fm->blocks < 0) +- old_blocks = buf.st_blocks; +- else +- old_blocks = fm->blocks; ++ return 1; ++} ++ ++static int _filemap_fd_check_changed(struct filemap_monitor *fm) ++{ ++ int64_t old_blocks; ++ ++ old_blocks = fm->blocks; + +- fm->blocks = blocks; ++ if (!_filemap_fd_update_blocks(fm)) ++ return -1; + + return (fm->blocks != old_blocks); + } +@@ -564,6 +565,7 @@ static int _filemap_monitor_check_file_unlinked(struct filemap_monitor *fm) + ssize_t len; + + fm->deleted = 0; ++ same = 0; + + if ((fd = open(fm->path, O_RDONLY)) < 0) + goto check_unlinked; +@@ -684,7 +686,10 @@ static int _update_regions(struct dm_stats *dms, struct filemap_monitor *fm) + for (region = regions; *region != DM_STATS_REGIONS_ALL; region++) + nr_regions++; + +- if (regions[0] != fm->group_id) { ++ if (!nr_regions) ++ log_warn("File contains no extents: exiting."); ++ ++ if (nr_regions && (regions[0] != fm->group_id)) { + log_warn("group_id changed from " FMTu64 " to " FMTu64, + fm->group_id, regions[0]); + fm->group_id = regions[0]; +@@ -715,6 +720,9 @@ static int _dmfilemapd(struct filemap_monitor *fm) + if (!_filemap_monitor_set_notify(fm)) + goto bad; + ++ if (!_filemap_fd_update_blocks(fm)) ++ goto bad; ++ + if (!dm_stats_list(dms, DM_STATS_ALL_PROGRAMS)) { + log_error("Failed to list stats handle."); + goto bad; +@@ -748,17 +756,16 @@ static int _dmfilemapd(struct filemap_monitor *fm) + if ((check = _filemap_fd_check_changed(fm)) < 0) + goto bad; + +- if (!check) +- goto wait; +- +- if (!_update_regions(dms, fm)) ++ if (check && !_update_regions(dms, fm)) + goto bad; + ++ running = !!fm->nr_regions; ++ if (!running) ++ continue; ++ + wait: + _filemap_monitor_wait(FILEMAPD_WAIT_USECS); + +- running = !!fm->nr_regions; +- + /* mode=inode termination condions */ + if (fm->mode == DM_FILEMAPD_FOLLOW_INODE) { + if (!_filemap_monitor_check_file_unlinked(fm)) +@@ -801,6 +808,8 @@ int main(int argc, char **argv) + { + struct filemap_monitor fm; + ++ memset(&fm, 0, sizeof(fm)); ++ + if (!_parse_args(argc, argv, &fm)) { + dm_free(fm.path); + return 1; +diff --git a/daemons/lvmdbusd/automatedproperties.py b/daemons/lvmdbusd/automatedproperties.py +index 68cea6e..e188fa4 100644 +--- a/daemons/lvmdbusd/automatedproperties.py ++++ b/daemons/lvmdbusd/automatedproperties.py +@@ -100,7 +100,7 @@ class AutomatedProperties(dbus.service.Object): + raise dbus.exceptions.DBusException( + obj._ap_interface, + 'The object %s does not implement the %s interface' +- % (self.__class__, interface_name)) ++ % (obj.__class__, interface_name)) + + @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, + in_signature='s', out_signature='a{sv}', +diff --git a/daemons/lvmdbusd/background.py b/daemons/lvmdbusd/background.py +index f7f77d5..90e8b68 100644 +--- a/daemons/lvmdbusd/background.py ++++ b/daemons/lvmdbusd/background.py +@@ -9,12 +9,13 @@ + + import subprocess + from . import cfg +-from .cmdhandler import options_to_cli_args ++from .cmdhandler import options_to_cli_args, LvmExecutionMeta + import dbus + from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug,\ + add_no_notify + import os + import threading ++import time + + + def pv_move_lv_cmd(move_options, lv_full_name, +@@ -47,6 +48,11 @@ def _move_merge(interface_name, command, job_state): + # Instruct lvm to not register an event with us + command = add_no_notify(command) + ++ #(self, start, ended, cmd, ec, stdout_txt, stderr_txt) ++ meta = LvmExecutionMeta(time.time(), 0, command, -1000, None, None) ++ ++ cfg.blackbox.add(meta) ++ + process = subprocess.Popen(command, stdout=subprocess.PIPE, + env=os.environ, + stderr=subprocess.PIPE, close_fds=True) +@@ -74,6 +80,11 @@ def _move_merge(interface_name, command, job_state): + + out = process.communicate() + ++ with meta.lock: ++ meta.ended = time.time() ++ meta.ec = process.returncode ++ meta.stderr_txt = out[1] ++ + if process.returncode == 0: + job_state.Percent = 100 + else: +diff --git a/daemons/lvmdbusd/cmdhandler.py b/daemons/lvmdbusd/cmdhandler.py +index 8ed38cb..4fb1670 100644 +--- a/daemons/lvmdbusd/cmdhandler.py ++++ b/daemons/lvmdbusd/cmdhandler.py +@@ -37,6 +37,7 @@ cmd_lock = threading.RLock() + class LvmExecutionMeta(object): + + def __init__(self, start, ended, cmd, ec, stdout_txt, stderr_txt): ++ self.lock = threading.RLock() + self.start = start + self.ended = ended + self.cmd = cmd +@@ -45,12 +46,13 @@ class LvmExecutionMeta(object): + self.stderr_txt = stderr_txt + + def __str__(self): +- return "EC= %d for %s\n" \ +- "STARTED: %f, ENDED: %f\n" \ +- "STDOUT=%s\n" \ +- "STDERR=%s\n" % \ +- (self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt, +- self.stderr_txt) ++ with self.lock: ++ return "EC= %d for %s\n" \ ++ "STARTED: %f, ENDED: %f\n" \ ++ "STDOUT=%s\n" \ ++ "STDERR=%s\n" % \ ++ (self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt, ++ self.stderr_txt) + + + class LvmFlightRecorder(object): +@@ -618,10 +620,10 @@ def vg_reduce(vg_name, missing, pv_devices, reduce_options): + cmd = ['vgreduce'] + cmd.extend(options_to_cli_args(reduce_options)) + +- if len(pv_devices) == 0: +- cmd.append('--all') + if missing: + cmd.append('--removemissing') ++ elif len(pv_devices) == 0: ++ cmd.append('--all') + + cmd.append(vg_name) + cmd.extend(pv_devices) +diff --git a/daemons/lvmdbusd/fetch.py b/daemons/lvmdbusd/fetch.py +index 78c3a4b..c053685 100644 +--- a/daemons/lvmdbusd/fetch.py ++++ b/daemons/lvmdbusd/fetch.py +@@ -82,10 +82,10 @@ class StateUpdate(object): + + @staticmethod + def update_thread(obj): ++ queued_requests = [] + while cfg.run.value != 0: + # noinspection PyBroadException + try: +- queued_requests = [] + refresh = True + emit_signal = True + cache_refresh = True +@@ -96,7 +96,7 @@ class StateUpdate(object): + wait = not obj.deferred + obj.deferred = False + +- if wait: ++ if len(queued_requests) == 0 and wait: + queued_requests.append(obj.queue.get(True, 2)) + + # Ok we have one or the deferred queue has some, +@@ -131,11 +131,17 @@ class StateUpdate(object): + for i in queued_requests: + i.set_result(num_changes) + ++ # Only clear out the requests after we have given them a result ++ # otherwise we can orphan the waiting threads and they never ++ # wake up if we get an exception ++ queued_requests = [] ++ + except queue.Empty: + pass + except Exception: + st = traceback.format_exc() + log_error("update_thread exception: \n%s" % st) ++ cfg.blackbox.dump() + + def __init__(self): + self.lock = threading.RLock() +diff --git a/daemons/lvmdbusd/objectmanager.py b/daemons/lvmdbusd/objectmanager.py +index a9d13a7..563b9ec 100644 +--- a/daemons/lvmdbusd/objectmanager.py ++++ b/daemons/lvmdbusd/objectmanager.py +@@ -223,8 +223,9 @@ class ObjectManager(AutomatedProperties): + :param lvm_id: The lvm identifier + """ + with self.rlock: +- if lvm_id in self._id_to_object_path: +- return self.get_object_by_path(self._id_to_object_path[lvm_id]) ++ lookup_rc = self._id_lookup(lvm_id) ++ if lookup_rc: ++ return self.get_object_by_path(lookup_rc) + return None + + def get_object_path_by_lvm_id(self, lvm_id): +@@ -234,8 +235,9 @@ class ObjectManager(AutomatedProperties): + :return: Object path or '/' if not found + """ + with self.rlock: +- if lvm_id in self._id_to_object_path: +- return self._id_to_object_path[lvm_id] ++ lookup_rc = self._id_lookup(lvm_id) ++ if lookup_rc: ++ return lookup_rc + return '/' + + def _uuid_verify(self, path, uuid, lvm_id): +diff --git a/daemons/lvmdbusd/utils.py b/daemons/lvmdbusd/utils.py +index af9e10a..170824d 100644 +--- a/daemons/lvmdbusd/utils.py ++++ b/daemons/lvmdbusd/utils.py +@@ -519,7 +519,9 @@ def add_no_notify(cmdline): + if '--config' in cmdline: + for i, arg in enumerate(cmdline): + if arg == '--config': +- cmdline[i] += "global/notify_dbus=0" ++ if len(cmdline) <= i+1: ++ raise dbus.exceptions.DBusException("Missing value for --config option.") ++ cmdline[i+1] += " global/notify_dbus=0" + break + else: + cmdline.extend(['--config', 'global/notify_dbus=0']) +diff --git a/lib/activate/activate.c b/lib/activate/activate.c +index a3978ad..282dd4f 100644 +--- a/lib/activate/activate.c ++++ b/lib/activate/activate.c +@@ -835,7 +835,6 @@ int lv_info_with_seg_status(struct cmd_context *cmd, + * When merge is in progress, query merging origin LV instead. + * COW volume is already mapped as error target in this case. + */ +- status->lv = olv; + return 1; + } + +diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h +index 3b0eebb..40b64ab 100644 +--- a/lib/config/config_settings.h ++++ b/lib/config/config_settings.h +@@ -469,8 +469,9 @@ cfg(allocation_mirror_logs_require_separate_pvs_CFG, "mirror_logs_require_separa + + cfg(allocation_raid_stripe_all_devices_CFG, "raid_stripe_all_devices", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_ALLOCATION_STRIPE_ALL_DEVICES, vsn(2, 2, 162), NULL, 0, NULL, + "Stripe across all PVs when RAID stripes are not specified.\n" +- "If enabled, all PVs in the VG or on the command line are used for raid0/4/5/6/10\n" +- "when the command does not specify the number of stripes to use.\n" ++ "If enabled, all PVs in the VG or on the command line are used for\n" ++ "raid0/4/5/6/10 when the command does not specify the number of\n" ++ "stripes to use.\n" + "This was the default behaviour until release 2.02.162.\n") + + cfg(allocation_cache_pool_metadata_require_separate_pvs_CFG, "cache_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA, CFG_TYPE_BOOL, DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 106), NULL, 0, NULL, +@@ -934,7 +935,7 @@ cfg(global_use_lvmetad_CFG, "use_lvmetad", global_CFG_SECTION, 0, CFG_TYPE_BOOL, + "devices/global_filter.\n") + + cfg(global_lvmetad_update_wait_time_CFG, "lvmetad_update_wait_time", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_LVMETAD_UPDATE_WAIT_TIME, vsn(2, 2, 151), NULL, 0, NULL, +- "The number of seconds a command will wait for lvmetad update to finish.\n" ++ "Number of seconds a command will wait for lvmetad update to finish.\n" + "After waiting for this period, a command will not use lvmetad, and\n" + "will revert to disk scanning.\n") + +diff --git a/lib/config/defaults.h b/lib/config/defaults.h +index 65b81f1..d9e19d9 100644 +--- a/lib/config/defaults.h ++++ b/lib/config/defaults.h +@@ -104,9 +104,9 @@ + #define DEFAULT_THIN_REPAIR_OPTION1 "" + #define DEFAULT_THIN_REPAIR_OPTIONS_CONFIG "#S" DEFAULT_THIN_REPAIR_OPTION1 + #define DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS 0 +-#define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */ ++#define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (DM_THIN_MAX_METADATA_SIZE / 2) /* KB */ + #define DEFAULT_THIN_POOL_MIN_METADATA_SIZE 2048 /* KB */ +-#define DEFAULT_THIN_POOL_OPTIMAL_SIZE (128 * 1024 * 1024) /* KB */ ++#define DEFAULT_THIN_POOL_OPTIMAL_METADATA_SIZE (128 * 1024) /* KB */ + #define DEFAULT_THIN_POOL_CHUNK_SIZE_POLICY "generic" + #define DEFAULT_THIN_POOL_CHUNK_SIZE 64 /* KB */ + #define DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE 512 /* KB */ +diff --git a/lib/format_text/export.c b/lib/format_text/export.c +index 473275d..f369089 100644 +--- a/lib/format_text/export.c ++++ b/lib/format_text/export.c +@@ -358,11 +358,12 @@ static int _print_header(struct cmd_context *cmd, struct formatter *f, + static int _print_flag_config(struct formatter *f, uint64_t status, int type) + { + char buffer[4096]; +- if (!print_flags(status, type | STATUS_FLAG, buffer, sizeof(buffer))) ++ ++ if (!print_flags(buffer, sizeof(buffer), type, STATUS_FLAG, status)) + return_0; + outf(f, "status = %s", buffer); + +- if (!print_flags(status, type, buffer, sizeof(buffer))) ++ if (!print_flags(buffer, sizeof(buffer), type, COMPATIBLE_FLAG, status)) + return_0; + outf(f, "flags = %s", buffer); + +@@ -501,7 +502,13 @@ static int _print_vg(struct formatter *f, struct volume_group *vg) + */ + static const char *_get_pv_name_from_uuid(struct formatter *f, char *uuid) + { +- return dm_hash_lookup(f->pv_names, uuid); ++ const char *pv_name = dm_hash_lookup(f->pv_names, uuid); ++ ++ if (!pv_name) ++ log_error(INTERNAL_ERROR "PV name for uuid %s missing from text metadata export hash table.", ++ uuid); ++ ++ return pv_name; + } + + static const char *_get_pv_name(struct formatter *f, struct physical_volume *pv) +@@ -577,6 +584,11 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg) + static int _print_segment(struct formatter *f, struct volume_group *vg, + int count, struct lv_segment *seg) + { ++ char buffer[2048]; ++ ++ if (!print_segtype_lvflags(buffer, sizeof(buffer), seg->lv->status)) ++ return_0; ++ + outf(f, "segment%u {", count); + _inc_indent(f); + +@@ -587,7 +599,8 @@ static int _print_segment(struct formatter *f, struct volume_group *vg, + if (seg->reshape_len) + outsize(f, (uint64_t) seg->reshape_len * vg->extent_size, + "reshape_count = %u", seg->reshape_len); +- outf(f, "type = \"%s\"", seg->segtype->name); ++ ++ outf(f, "type = \"%s%s\"", seg->segtype->name, buffer); + + if (!_out_list(f, &seg->tags, "tags")) + return_0; +@@ -607,6 +620,7 @@ int out_areas(struct formatter *f, const struct lv_segment *seg, + { + const char *name; + unsigned int s; ++ struct physical_volume *pv; + + outnl(f); + +@@ -616,7 +630,13 @@ int out_areas(struct formatter *f, const struct lv_segment *seg, + for (s = 0; s < seg->area_count; s++) { + switch (seg_type(seg, s)) { + case AREA_PV: +- if (!(name = _get_pv_name(f, seg_pv(seg, s)))) ++ if (!(pv = seg_pv(seg, s))) { ++ log_error(INTERNAL_ERROR "Missing PV for area %" PRIu32 " of %s segment of LV %s.", ++ s, type, display_lvname(seg->lv)); ++ return 0; ++ } ++ ++ if (!(name = _get_pv_name(f, pv))) + return_0; + + outf(f, "\"%s\", %u%s", name, +@@ -650,6 +670,8 @@ int out_areas(struct formatter *f, const struct lv_segment *seg, + + break; + case AREA_UNASSIGNED: ++ log_error(INTERNAL_ERROR "Invalid type for area %" PRIu32 " of %s segment of LV %s.", ++ s, type, display_lvname(seg->lv)); + return 0; + } + } +diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c +index a9f81f5..88fb091 100644 +--- a/lib/format_text/flags.c ++++ b/lib/format_text/flags.c +@@ -47,6 +47,7 @@ static const struct flag _pv_flags[] = { + {ALLOCATABLE_PV, "ALLOCATABLE", STATUS_FLAG}, + {EXPORTED_VG, "EXPORTED", STATUS_FLAG}, + {MISSING_PV, "MISSING", COMPATIBLE_FLAG}, ++ {MISSING_PV, "MISSING", STATUS_FLAG}, + {UNLABELLED_PV, NULL, 0}, + {0, NULL, 0} + }; +@@ -61,13 +62,14 @@ static const struct flag _lv_flags[] = { + {LOCKED, "LOCKED", STATUS_FLAG}, + {LV_NOTSYNCED, "NOTSYNCED", STATUS_FLAG}, + {LV_REBUILD, "REBUILD", STATUS_FLAG}, +- {LV_RESHAPE_DELTA_DISKS_PLUS, "RESHAPE_DELTA_DISKS_PLUS", STATUS_FLAG}, +- {LV_RESHAPE_DELTA_DISKS_MINUS, "RESHAPE_DELTA_DISKS_MINUS", STATUS_FLAG}, +- {LV_REMOVE_AFTER_RESHAPE, "REMOVE_AFTER_RESHAPE", STATUS_FLAG}, ++ {LV_RESHAPE, "RESHAPE", SEGTYPE_FLAG}, ++ {LV_RESHAPE_DELTA_DISKS_PLUS, "RESHAPE_DELTA_DISKS_PLUS", SEGTYPE_FLAG}, ++ {LV_RESHAPE_DELTA_DISKS_MINUS, "RESHAPE_DELTA_DISKS_MINUS", SEGTYPE_FLAG}, ++ {LV_REMOVE_AFTER_RESHAPE, "REMOVE_AFTER_RESHAPE", SEGTYPE_FLAG}, + {LV_WRITEMOSTLY, "WRITEMOSTLY", STATUS_FLAG}, + {LV_ACTIVATION_SKIP, "ACTIVATION_SKIP", COMPATIBLE_FLAG}, + {LV_ERROR_WHEN_FULL, "ERROR_WHEN_FULL", COMPATIBLE_FLAG}, +- {LV_METADATA_FORMAT, "METADATA_FORMAT", STATUS_FLAG}, ++ {LV_METADATA_FORMAT, "METADATA_FORMAT", SEGTYPE_FLAG}, + {LV_NOSCAN, NULL, 0}, + {LV_TEMPORARY, NULL, 0}, + {POOL_METADATA_SPARE, NULL, 0}, +@@ -101,9 +103,9 @@ static const struct flag _lv_flags[] = { + {0, NULL, 0} + }; + +-static const struct flag *_get_flags(int type) ++static const struct flag *_get_flags(enum pv_vg_lv_e type) + { +- switch (type & ~STATUS_FLAG) { ++ switch (type) { + case VG_FLAGS: + return _vg_flags; + +@@ -114,7 +116,7 @@ static const struct flag *_get_flags(int type) + return _lv_flags; + } + +- log_error("Unknown flag set requested."); ++ log_error(INTERNAL_ERROR "Unknown flag set requested."); + return NULL; + } + +@@ -123,7 +125,7 @@ static const struct flag *_get_flags(int type) + * using one of the tables defined at the top of + * the file. + */ +-int print_flags(uint64_t status, int type, char *buffer, size_t size) ++int print_flags(char *buffer, size_t size, enum pv_vg_lv_e type, int mask, uint64_t status) + { + int f, first = 1; + const struct flag *flags; +@@ -132,13 +134,13 @@ int print_flags(uint64_t status, int type, char *buffer, size_t size) + return_0; + + if (!emit_to_buffer(&buffer, &size, "[")) +- return 0; ++ return_0; + + for (f = 0; flags[f].mask; f++) { + if (status & flags[f].mask) { + status &= ~flags[f].mask; + +- if ((type & STATUS_FLAG) != flags[f].kind) ++ if (mask != flags[f].kind) + continue; + + /* Internal-only flag? */ +@@ -147,18 +149,18 @@ int print_flags(uint64_t status, int type, char *buffer, size_t size) + + if (!first) { + if (!emit_to_buffer(&buffer, &size, ", ")) +- return 0; ++ return_0; + } else + first = 0; + + if (!emit_to_buffer(&buffer, &size, "\"%s\"", +- flags[f].description)) +- return 0; ++ flags[f].description)) ++ return_0; + } + } + + if (!emit_to_buffer(&buffer, &size, "]")) +- return 0; ++ return_0; + + if (status) + log_warn(INTERNAL_ERROR "Metadata inconsistency: " +@@ -167,9 +169,9 @@ int print_flags(uint64_t status, int type, char *buffer, size_t size) + return 1; + } + +-int read_flags(uint64_t *status, int type, const struct dm_config_value *cv) ++int read_flags(uint64_t *status, enum pv_vg_lv_e type, int mask, const struct dm_config_value *cv) + { +- int f; ++ unsigned f; + uint64_t s = UINT64_C(0); + const struct flag *flags; + +@@ -186,7 +188,8 @@ int read_flags(uint64_t *status, int type, const struct dm_config_value *cv) + } + + for (f = 0; flags[f].description; f++) +- if (!strcmp(flags[f].description, cv->v.str)) { ++ if ((flags[f].kind & mask) && ++ !strcmp(flags[f].description, cv->v.str)) { + s |= flags[f].mask; + break; + } +@@ -200,7 +203,7 @@ int read_flags(uint64_t *status, int type, const struct dm_config_value *cv) + * by this case. + */ + s |= PARTIAL_VG; +- } else if (!flags[f].description && (type & STATUS_FLAG)) { ++ } else if (!flags[f].description && (mask & STATUS_FLAG)) { + log_error("Unknown status flag '%s'.", cv->v.str); + return 0; + } +@@ -212,3 +215,71 @@ int read_flags(uint64_t *status, int type, const struct dm_config_value *cv) + *status |= s; + return 1; + } ++ ++/* ++ * Parse extra status flags from segment "type" string. ++ * These flags are seen as INCOMPATIBLE by any older lvm2 code. ++ * All flags separated by '+' are trimmed from passed string. ++ * All UNKNOWN flags will again cause the "UNKNOWN" segtype. ++ * ++ * Note: using these segtype status flags instead of actual ++ * status flags ensures wanted incompatiblity. ++ */ ++int read_segtype_lvflags(uint64_t *status, char *segtype_str) ++{ ++ unsigned i; ++ const struct flag *flags = _lv_flags; ++ char *delim; ++ char *flag, *buffer, *str; ++ ++ if (!(str = strchr(segtype_str, '+'))) ++ return 1; /* No flags */ ++ ++ if (!(buffer = dm_strdup(str + 1))) { ++ log_error("Cannot duplicate segment string."); ++ return 0; ++ } ++ ++ delim = buffer; ++ ++ do { ++ flag = delim; ++ if ((delim = strchr(delim, '+'))) ++ *delim++ = '\0'; ++ ++ for (i = 0; flags[i].description; i++) ++ if ((flags[i].kind & SEGTYPE_FLAG) && ++ !strcmp(flags[i].description, flag)) { ++ *status |= flags[i].mask; ++ break; ++ } ++ ++ } while (delim && flags[i].description); /* Till no more flags in type appear */ ++ ++ if (!flags[i].description) ++ /* Unknown flag is incompatible - returns unmodified segtype_str */ ++ log_warn("WARNING: Unrecognised flag %s in segment type %s.", ++ flag, segtype_str); ++ else ++ *str = '\0'; /* Cut away 1st. '+' */ ++ ++ dm_free(buffer); ++ ++ return 1; ++} ++ ++int print_segtype_lvflags(char *buffer, size_t size, uint64_t status) ++{ ++ unsigned i; ++ const struct flag *flags = _lv_flags; ++ ++ buffer[0] = 0; ++ for (i = 0; flags[i].mask; i++) ++ if ((flags[i].kind & SEGTYPE_FLAG) && ++ (status & flags[i].mask) && ++ !emit_to_buffer(&buffer, &size, "+%s", ++ flags[i].description)) ++ return 0; ++ ++ return 1; ++} +diff --git a/lib/format_text/import-export.h b/lib/format_text/import-export.h +index c081c51..4b47636 100644 +--- a/lib/format_text/import-export.h ++++ b/lib/format_text/import-export.h +@@ -35,14 +35,16 @@ + * VGs, PVs and LVs all have status bitsets, we gather together + * common code for reading and writing them. + */ +-enum { +- COMPATIBLE_FLAG = 0x0, ++enum pv_vg_lv_e { ++ PV_FLAGS = 1, + VG_FLAGS, +- PV_FLAGS, + LV_FLAGS, +- STATUS_FLAG = 0x8, + }; + ++#define COMPATIBLE_FLAG 0x01 ++#define STATUS_FLAG 0x02 ++#define SEGTYPE_FLAG 0x04 ++ + struct text_vg_version_ops { + int (*check_version) (const struct dm_config_tree * cf); + struct volume_group *(*read_vg) (struct format_instance * fid, +@@ -58,8 +60,11 @@ struct text_vg_version_ops { + + struct text_vg_version_ops *text_vg_vsn1_init(void); + +-int print_flags(uint64_t status, int type, char *buffer, size_t size); +-int read_flags(uint64_t *status, int type, const struct dm_config_value *cv); ++int print_flags(char *buffer, size_t size, enum pv_vg_lv_e type, int mask, uint64_t status); ++int read_flags(uint64_t *status, enum pv_vg_lv_e type, int mask, const struct dm_config_value *cv); ++ ++int print_segtype_lvflags(char *buffer, size_t size, uint64_t status); ++int read_segtype_lvflags(uint64_t *status, char *segtype_scr); + + int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp); + size_t text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf); +diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c +index e545008..7d9257e 100644 +--- a/lib/format_text/import_vsn1.c ++++ b/lib/format_text/import_vsn1.c +@@ -140,13 +140,14 @@ static int _read_flag_config(const struct dm_config_node *n, uint64_t *status, i + return 0; + } + +- if (!(read_flags(status, type | STATUS_FLAG, cv))) { ++ /* For backward compatible metadata accept both type of flags */ ++ if (!(read_flags(status, type, STATUS_FLAG | SEGTYPE_FLAG, cv))) { + log_error("Could not read status flags."); + return 0; + } + + if (dm_config_get_list(n, "flags", &cv)) { +- if (!(read_flags(status, type, cv))) { ++ if (!(read_flags(status, type, COMPATIBLE_FLAG, cv))) { + log_error("Could not read flags."); + return 0; + } +@@ -357,6 +358,7 @@ static int _read_segment(struct logical_volume *lv, const struct dm_config_node + uint32_t area_extents, start_extent, extent_count, reshape_count, data_copies; + struct segment_type *segtype; + const char *segtype_str; ++ char *segtype_with_flags; + + if (!sn_child) { + log_error("Empty segment section."); +@@ -388,9 +390,24 @@ static int _read_segment(struct logical_volume *lv, const struct dm_config_node + return 0; + } + +- if (!(segtype = get_segtype_from_string(lv->vg->cmd, segtype_str))) ++ /* Locally duplicate to parse out status flag bits */ ++ if (!(segtype_with_flags = dm_pool_strdup(mem, segtype_str))) { ++ log_error("Cannot duplicate segtype string."); ++ return 0; ++ } ++ ++ if (!read_segtype_lvflags(&lv->status, segtype_with_flags)) { ++ log_error("Couldn't read segtype for logical volume %s.", ++ display_lvname(lv)); ++ return 0; ++ } ++ ++ if (!(segtype = get_segtype_from_string(lv->vg->cmd, segtype_with_flags))) + return_0; + ++ /* Can drop temporary string here as nothing has allocated from VGMEM meanwhile */ ++ dm_pool_free(mem, segtype_with_flags); ++ + if (segtype->ops->text_import_area_count && + !segtype->ops->text_import_area_count(sn_child, &area_count)) + return_0; +diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c +index 3d82a05..e642459 100644 +--- a/lib/metadata/merge.c ++++ b/lib/metadata/merge.c +@@ -222,7 +222,7 @@ static void _check_non_raid_seg_members(struct lv_segment *seg, int *error_count + } + + /* +- * Check RAID segment sruct members of @seg for acceptable ++ * Check RAID segment struct members of @seg for acceptable + * properties and increment @error_count for any bogus ones. + */ + static void _check_raid_seg(struct lv_segment *seg, int *error_count) +diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h +index 8b4310b..c4bebd0 100644 +--- a/lib/metadata/metadata-exported.h ++++ b/lib/metadata/metadata-exported.h +@@ -142,7 +142,10 @@ + + #define LV_REMOVE_AFTER_RESHAPE UINT64_C(0x0400000000000000) /* LV needs to be removed after a shrinking reshape */ + #define LV_METADATA_FORMAT UINT64_C(0x0800000000000000) /* LV has segments with metadata format */ +-/* Next unused flag: UINT64_C(0x1000000000000000) */ ++ ++#define LV_RESHAPE UINT64_C(0x1000000000000000) /* Ongoing reshape (number of stripes, stripesize or raid algorithm change): ++ used as SEGTYPE_FLAG to prevent activation on old runtime */ ++/* Next unused flag: UINT64_C(0x2000000000000000) */ + + /* Format features flags */ + #define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */ +diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c +index 1b500ed..4ba81c7 100644 +--- a/lib/metadata/metadata.c ++++ b/lib/metadata/metadata.c +@@ -1089,8 +1089,7 @@ static struct volume_group *_vg_make_handle(struct cmd_context *cmd, + if (!vg && !(vg = alloc_vg("vg_make_handle", cmd, NULL))) + return_NULL; + +- if (vg->read_status != failure) +- vg->read_status = failure; ++ vg->read_status = failure; + + if (vg->fid && !_vg_update_vg_committed(vg)) + vg->read_status |= FAILED_ALLOCATION; +@@ -1122,6 +1121,7 @@ int vg_has_unknown_segments(const struct volume_group *vg) + struct volume_group *vg_lock_and_create(struct cmd_context *cmd, const char *vg_name) + { + uint32_t rc; ++ struct volume_group *vg; + + if (!validate_name(vg_name)) { + log_error("Invalid vg name %s", vg_name); +@@ -1134,7 +1134,11 @@ struct volume_group *vg_lock_and_create(struct cmd_context *cmd, const char *vg_ + /* NOTE: let caller decide - this may be check for existence */ + return _vg_make_handle(cmd, NULL, rc); + +- return vg_create(cmd, vg_name); ++ vg = vg_create(cmd, vg_name); ++ if (!vg || vg_read_error(vg)) ++ unlock_vg(cmd, NULL, vg_name); ++ ++ return vg; + } + + /* +@@ -3707,6 +3711,37 @@ struct _vg_read_orphan_baton { + int repair; + }; + ++/* ++ * If we know that the PV is orphan, meaning there's at least one MDA on ++ * that PV which does not reference any VG and at the same time there's ++ * PV_EXT_USED flag set, we're certainly in an inconsistent state and we ++ * need to fix this. ++ * ++ * For example, such situation can happen during vgremove/vgreduce if we ++ * removed/reduced the VG, but we haven't written PV headers yet because ++ * vgremove stopped abruptly for whatever reason just before writing new ++ * PV headers with updated state, including PV extension flags (and so the ++ * PV_EXT_USED flag). ++ * ++ * However, in case the PV has no MDAs at all, we can't double-check ++ * whether the PV_EXT_USED is correct or not - if that PV is marked ++ * as used, it's either: ++ * - really used (but other disks with MDAs are missing) ++ * - or the error state as described above is hit ++ * ++ * User needs to overwrite the PV header directly if it's really clear ++ * the PV having no MDAs does not belong to any VG and at the same time ++ * it's still marked as being in use (pvcreate -ff will fix this). ++ * ++ * Note that the above doesn't account for the case where the PV has ++ * VG metadata that fails to be parsed. In that case, the PV looks ++ * like an in-use orphan, and is auto-repaired here. A PV with ++ * unparsable metadata should be kept on a special list of devices ++ * (like duplicate PVs) that are not auto-repaired, cannot be used ++ * by pvcreate, and are displayed with a special flag by 'pvs'. ++ */ ++ ++#if 0 + static int _check_or_repair_orphan_pv_ext(struct physical_volume *pv, + struct lvmcache_info *info, + struct _vg_read_orphan_baton *b) +@@ -3760,12 +3795,15 @@ static int _check_or_repair_orphan_pv_ext(struct physical_volume *pv, + + return 1; + } ++#endif + + static int _vg_read_orphan_pv(struct lvmcache_info *info, void *baton) + { + struct _vg_read_orphan_baton *b = baton; + struct physical_volume *pv = NULL; + struct pv_list *pvl; ++ uint32_t ext_version; ++ uint32_t ext_flags; + + if (!(pv = _pv_read(b->vg->cmd, b->vg->vgmem, dev_name(lvmcache_device(info)), + b->vg->fid, b->warn_flags, 0))) { +@@ -3781,10 +3819,59 @@ static int _vg_read_orphan_pv(struct lvmcache_info *info, void *baton) + pvl->pv = pv; + add_pvl_to_vgs(b->vg, pvl); + ++ /* ++ * FIXME: this bit of code that does the auto repair is disabled ++ * until we can distinguish cases where the repair should not ++ * happen, i.e. the VG metadata could not be read/parsed. ++ * ++ * A PV holding VG metadata that lvm can't understand ++ * (e.g. damaged, checksum error, unrecognized flag) ++ * will appear as an in-use orphan, and would be cleared ++ * by this repair code. Disable this repair until the ++ * code can keep track of these problematic PVs, and ++ * distinguish them from actual in-use orphans. ++ */ ++ ++ /* + if (!_check_or_repair_orphan_pv_ext(pv, info, baton)) { + stack; + return 0; + } ++ */ ++ ++ /* ++ * Nothing to do if PV header extension < 2: ++ * - version 0 is PV header without any extensions, ++ * - version 1 has bootloader area support only and ++ * we're not checking anything for that one here. ++ */ ++ ext_version = lvmcache_ext_version(info); ++ ext_flags = lvmcache_ext_flags(info); ++ ++ /* ++ * Warn about a PV that has the in-use flag set, but appears in ++ * the orphan VG (no VG was found referencing it.) ++ * There are a number of conditions that could lead to this: ++ * ++ * . The PV was created with no mdas and is used in a VG with ++ * other PVs (with metadata) that have not yet appeared on ++ * the system. So, no VG metadata is found by lvm which ++ * references the in-use PV with no mdas. ++ * ++ * . vgremove could have failed after clearing mdas but ++ * before clearing the in-use flag. In this case, the ++ * in-use flag needs to be manually cleared on the PV. ++ * ++ * . The PV may have damanged/unrecognized VG metadata ++ * that lvm could not read. ++ * ++ * . The PV may have no mdas, and the PVs with the metadata ++ * may have damaged/unrecognized metadata. ++ */ ++ if ((ext_version >= 2) && (ext_flags & PV_EXT_USED)) { ++ log_warn("WARNING: PV %s is marked in use but no VG was found using it.", pv_dev_name(pv)); ++ log_warn("WARNING: PV %s might need repairing.", pv_dev_name(pv)); ++ } + + return 1; + } +@@ -3910,7 +3997,13 @@ static int _check_reappeared_pv(struct volume_group *correct_vg, + * confusing. + */ + if (correct_vg->cmd->handles_missing_pvs) +- return rv; ++ return rv; ++ ++ /* ++ * Skip this if there is no underlying device present for this PV. ++ */ ++ if (!pv->dev) ++ return rv; + + dm_list_iterate_items(pvl, &correct_vg->pvs) + if (pv->dev == pvl->pv->dev && is_missing_pv(pvl->pv)) { +@@ -4039,6 +4132,7 @@ static int _check_or_repair_pv_ext(struct cmd_context *cmd, + struct volume_group *vg, + int repair, int *inconsistent_pvs) + { ++ char uuid[64] __attribute__((aligned(8))); + struct lvmcache_info *info; + uint32_t ext_version, ext_flags; + struct pv_list *pvl; +@@ -4052,6 +4146,14 @@ static int _check_or_repair_pv_ext(struct cmd_context *cmd, + if (is_missing_pv(pvl->pv)) + continue; + ++ if (!pvl->pv->dev) { ++ /* is_missing_pv doesn't catch NULL dev */ ++ memset(&uuid, 0, sizeof(uuid)); ++ id_write_format(&pvl->pv->id, uuid, sizeof(uuid)); ++ log_warn("WARNING: Not repairing PV %s with missing device.", uuid); ++ continue; ++ } ++ + if (!(info = lvmcache_info_from_pvid(pvl->pv->dev->pvid, pvl->pv->dev, 0))) { + log_error("Failed to find cached info for PV %s.", pv_dev_name(pvl->pv)); + goto out; +@@ -4165,8 +4267,7 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, + if (lvmetad_used() && !use_precommitted) { + if ((correct_vg = lvmcache_get_vg(cmd, vgname, vgid, precommitted))) { + dm_list_iterate_items(pvl, &correct_vg->pvs) +- if (pvl->pv->dev) +- reappeared += _check_reappeared_pv(correct_vg, pvl->pv, *consistent); ++ reappeared += _check_reappeared_pv(correct_vg, pvl->pv, *consistent); + if (reappeared && *consistent) + *consistent = _repair_inconsistent_vg(correct_vg); + else +@@ -5863,7 +5964,11 @@ static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const cha + if (failure) + goto_bad; + +- return _vg_make_handle(cmd, vg, failure); ++ if (!(vg = _vg_make_handle(cmd, vg, failure)) || vg_read_error(vg)) ++ if (!already_locked) ++ unlock_vg(cmd, vg, vg_name); ++ ++ return vg; + + bad: + if (!already_locked) +@@ -5924,7 +6029,12 @@ struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_name, + const char *vgid, uint32_t read_flags, uint32_t lockd_state) + { +- return vg_read(cmd, vg_name, vgid, read_flags | READ_FOR_UPDATE, lockd_state); ++ struct volume_group *vg = vg_read(cmd, vg_name, vgid, read_flags | READ_FOR_UPDATE, lockd_state); ++ ++ if (!vg || vg_read_error(vg)) ++ stack; ++ ++ return vg; + } + + /* +@@ -5953,9 +6063,8 @@ uint32_t vg_read_error(struct volume_group *vg_handle) + */ + uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname) + { +- if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) { ++ if (!lock_vol(cmd, vgname, LCK_VG_WRITE, NULL)) + return FAILED_LOCKING; +- } + + /* Find the vgname in the cache */ + /* If it's not there we must do full scan to be completely sure */ +diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c +index b708442..52def78 100644 +--- a/lib/metadata/raid_manip.c ++++ b/lib/metadata/raid_manip.c +@@ -57,6 +57,25 @@ static int _reshape_is_supported(struct cmd_context *cmd, const struct segment_t + } + + /* ++ * Check if rebuild CTR args are allowed when other images exist in the array ++ * with empty metadata areas for this kernel. ++ */ ++static int _rebuild_with_emptymeta_is_supported(struct cmd_context *cmd, ++ const struct segment_type *segtype) ++{ ++ unsigned attrs; ++ ++ if (!segtype->ops->target_present || ++ !segtype->ops->target_present(cmd, NULL, &attrs) || ++ !(attrs & RAID_FEATURE_NEW_DEVICES_ACCEPT_REBUILD)) { ++ log_verbose("RAID module does not support rebuild+emptymeta."); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/* + * Ensure region size exceeds the minimum for @lv because + * MD's bitmap is limited to tracking 2^21 regions. + * +@@ -285,6 +304,17 @@ static int _deactivate_and_remove_lvs(struct volume_group *vg, struct dm_list *r + struct lv_list *lvl; + + dm_list_iterate_items(lvl, removal_lvs) { ++ if (!lv_is_visible(lvl->lv)) { ++ log_error(INTERNAL_ERROR ++ "LVs must be set visible before removing."); ++ return 0; ++ } ++ /* Got to get any cluster lock an SubLVs to be removed. */ ++ if (!activate_lv_excl_local(vg->cmd, lvl->lv)) ++ return_0; ++ } ++ ++ dm_list_iterate_items(lvl, removal_lvs) { + if (!deactivate_lv(vg->cmd, lvl->lv)) + return_0; + if (!lv_remove(lvl->lv)) +@@ -379,23 +409,6 @@ int lv_raid_in_sync(const struct logical_volume *lv) + return _raid_in_sync(lv); + } + +-/* Check if RaidLV @lv is synced or any raid legs of @lv are not synced */ +-static int _raid_devs_sync_healthy(struct logical_volume *lv) +-{ +- char *raid_health; +- +- if (!_raid_in_sync(lv)) +- return 0; +- +- if (!seg_is_raid1(first_seg(lv))) +- return 1; +- +- if (!lv_raid_dev_health(lv, &raid_health)) +- return_0; +- +- return (strchr(raid_health, 'a') || strchr(raid_health, 'D')) ? 0 : 1; +-} +- + /* + * _raid_remove_top_layer + * @lv +@@ -427,8 +440,10 @@ static int _raid_remove_top_layer(struct logical_volume *lv, + return 0; + } + +- if (!(lvl_array = dm_pool_alloc(lv->vg->vgmem, 2 * sizeof(*lvl)))) ++ if (!(lvl_array = dm_pool_alloc(lv->vg->vgmem, 2 * sizeof(*lvl)))) { ++ log_error("Memory allocation failed."); + return_0; ++ } + + /* Add last metadata area to removal_lvs */ + lvl_array[0].lv = seg_metalv(seg, 0); +@@ -786,8 +801,10 @@ static int _reorder_raid10_near_seg_areas(struct lv_segment *seg, enum raid0_rai + /* FIXME: once more data copies supported with raid10 */ + stripes /= data_copies; + +- if (!(idx = dm_pool_zalloc(seg_lv(seg, 0)->vg->vgmem, seg->area_count * sizeof(*idx)))) +- return 0; ++ if (!(idx = dm_pool_zalloc(seg_lv(seg, 0)->vg->vgmem, seg->area_count * sizeof(*idx)))) { ++ log_error("Memory allocation failed."); ++ return_0; ++ } + + /* Set up positional index array */ + switch (conv) { +@@ -1056,8 +1073,10 @@ static int _alloc_image_components(struct logical_volume *lv, + const char *raid_segtype; + + if (!(lvl_array = dm_pool_alloc(lv->vg->vgmem, +- sizeof(*lvl_array) * count * 2))) ++ sizeof(*lvl_array) * count * 2))) { ++ log_error("Memory allocation failed."); + return_0; ++ } + + if (!(parallel_areas = build_parallel_areas_from_lv(lv, 0, 1))) + return_0; +@@ -1250,6 +1269,10 @@ static int _cmp_level(const struct segment_type *t1, const struct segment_type * + (!segtype_is_any_raid10(t1) && segtype_is_any_raid10(t2))) + return 0; + ++ if ((segtype_is_raid4(t1) && segtype_is_raid5_n(t2)) || ++ (segtype_is_raid5_n(t1) && segtype_is_raid4(t2))) ++ return 1; ++ + return !strncmp(t1->name, t2->name, 5); + } + +@@ -1627,6 +1650,8 @@ static int _lv_free_reshape_space_with_status(struct logical_volume *lv, enum al + } else if (where_it_was) + *where_it_was = alloc_none; + ++ lv->status &= ~LV_RESHAPE; ++ + return 1; + } + +@@ -1834,6 +1859,9 @@ static int _raid_reshape_add_images(struct logical_volume *lv, + + seg->stripe_size = new_stripe_size; + ++ /* Define image adding reshape (used as SEGTYPE_FLAG to avoid incompatible activations on old runtime) */ ++ lv->status |= LV_RESHAPE; ++ + return 1; + } + +@@ -1933,6 +1961,8 @@ static int _raid_reshape_remove_images(struct logical_volume *lv, + if (seg_is_any_raid5(seg) && new_image_count == 2) + seg->data_copies = 2; + ++ /* Define image removing reshape (used as SEGTYPE_FLAG to avoid incompatible activations on old runtime) */ ++ lv ->status |= LV_RESHAPE; + break; + + case 1: +@@ -1971,7 +2001,6 @@ static int _raid_reshape_remove_images(struct logical_volume *lv, + return 0; + + seg->area_count = new_image_count; +- + break; + + default: +@@ -1986,10 +2015,11 @@ static int _raid_reshape_remove_images(struct logical_volume *lv, + /* + * HM Helper: + * +- * Reshape: keep images in RAID @lv but change stripe size or data copies ++ * Reshape: keep images in RAID @lv but change layout, stripe size or data copies + * + */ + static const char *_get_segtype_alias(const struct segment_type *segtype); ++static const char *_get_segtype_alias_str(const struct logical_volume *lv, const struct segment_type *segtype); + static int _raid_reshape_keep_images(struct logical_volume *lv, + const struct segment_type *new_segtype, + int yes, int force, int *force_repair, +@@ -2001,10 +2031,13 @@ static int _raid_reshape_keep_images(struct logical_volume *lv, + struct lv_segment *seg = first_seg(lv); + + if (seg->segtype != new_segtype) +- log_print_unless_silent("Converting %s LV %s to %s.", +- lvseg_name(seg), display_lvname(lv), new_segtype->name); +- if (!yes && yes_no_prompt("Are you sure you want to convert %s LV %s to %s? [y/n]: ", +- lvseg_name(seg), display_lvname(lv), new_segtype->name) == 'n') { ++ log_print_unless_silent("Converting %s%s LV %s to %s%s.", ++ lvseg_name(seg), _get_segtype_alias_str(lv, seg->segtype), ++ display_lvname(lv), new_segtype->name, ++ _get_segtype_alias_str(lv, new_segtype)); ++ ++ if (!yes && yes_no_prompt("Are you sure you want to convert %s LV %s? [y/n]: ", ++ lvseg_name(seg), display_lvname(lv)) == 'n') { + log_error("Logical volume %s NOT converted.", display_lvname(lv)); + return 0; + } +@@ -2022,12 +2055,9 @@ static int _raid_reshape_keep_images(struct logical_volume *lv, + * The dm-raid target is able to use the space whereever it + * is found by appropriately selecting forward or backward reshape. + */ +- if (seg->segtype != new_segtype) { +- const char *alias = _get_segtype_alias(seg->segtype); +- +- if (!strcmp(alias, new_segtype->name)) +- alloc_reshape_space = 0; +- } ++ if (seg->segtype != new_segtype && ++ !strcmp(_get_segtype_alias(seg->segtype), new_segtype->name)) ++ alloc_reshape_space = 0; + + if (seg->stripe_size != new_stripe_size) + alloc_reshape_space = 1; +@@ -2043,6 +2073,9 @@ static int _raid_reshape_keep_images(struct logical_volume *lv, + + seg->segtype = new_segtype; + ++ /* Define stripesize/raid algorithm reshape (used as SEGTYPE_FLAG to avoid incompatible activations on old runtime) */ ++ lv->status |= LV_RESHAPE; ++ + return 1; + } + +@@ -2226,6 +2259,8 @@ static int _raid_reshape(struct logical_volume *lv, + return 0; + } + ++ lv->status &= ~LV_RESHAPE; /* Reset any reshaping segtype flag */ ++ + dm_list_init(&removal_lvs); + + /* No change in layout requested ? */ +@@ -2311,6 +2346,12 @@ static int _raid_reshape(struct logical_volume *lv, + + /* Handle disk addition reshaping */ + if (old_image_count < new_image_count) { ++ /* FIXME: remove once MD kernel rhbz1443999 got fixed. */ ++ if (sysconf(_SC_NPROCESSORS_ONLN) < 2) { ++ log_error("Can't add stripes to LV %s on single core.", display_lvname(lv)); ++ return 0; ++ } ++ + if (!_raid_reshape_add_images(lv, new_segtype, yes, + old_image_count, new_image_count, + new_stripes, new_stripe_size, allocate_pvs)) +@@ -2318,6 +2359,12 @@ static int _raid_reshape(struct logical_volume *lv, + + /* Handle disk removal reshaping */ + } else if (old_image_count > new_image_count) { ++ /* FIXME: remove once MD kernel rhbz1443999 got fixed. */ ++ if (sysconf(_SC_NPROCESSORS_ONLN) < 2) { ++ log_error("Can't remove stripes from LV %s on single core.", display_lvname(lv)); ++ return 0; ++ } ++ + if (!_raid_reshape_remove_images(lv, new_segtype, yes, force, + old_image_count, new_image_count, + new_stripes, new_stripe_size, +@@ -2505,6 +2552,7 @@ static int _raid_add_images_without_commit(struct logical_volume *lv, + struct dm_list meta_lvs, data_lvs; + struct lv_list *lvl; + struct lv_segment_area *new_areas; ++ struct segment_type *segtype; + + if (lv_is_not_synced(lv)) { + log_error("Can't add image to out-of-sync RAID LV:" +@@ -2536,8 +2584,19 @@ static int _raid_add_images_without_commit(struct logical_volume *lv, + * LV to accompany it. + */ + if (seg_is_linear(seg)) { +- /* A complete resync will be done, no need to mark each sub-lv */ +- status_mask = ~(LV_REBUILD); ++ /* ++ * As of dm-raid version 1.9.0, it is possible to specify ++ * RAID table lines with the 'rebuild' parameters necessary ++ * to force a "recover" instead of a "resync" on upconvert. ++ * ++ * LVM's interaction with older kernels should be as before - ++ * performing a complete resync rather than a set of rebuilds. ++ */ ++ if (!(segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_RAID1))) ++ return_0; ++ ++ if (!_rebuild_with_emptymeta_is_supported(lv->vg->cmd, segtype)) ++ status_mask = ~(LV_REBUILD); + + /* FIXME: allow setting region size on upconvert from linear */ + seg->region_size = get_default_region_size(lv->vg->cmd); +@@ -2803,6 +2862,87 @@ static int _extract_image_components(struct lv_segment *seg, uint32_t idx, + } + + /* ++ * _raid_allow_extraction ++ * @lv ++ * @extract_count ++ * @target_pvs ++ * ++ * returns: 0 if no, 1 if yes ++ */ ++static int _raid_allow_extraction(struct logical_volume *lv, ++ int extract_count, ++ struct dm_list *target_pvs) ++{ ++ int s, redundancy = 0; ++ char *dev_health; ++ char *sync_action; ++ struct lv_segment *seg = first_seg(lv); ++ struct cmd_context *cmd = lv->vg->cmd; ++ ++ /* If in-sync or hanlding repairs, allow to proceed. */ ++ if (_raid_in_sync(lv) || lv->vg->cmd->handles_missing_pvs) ++ return 1; ++ ++ /* ++ * FIXME: ++ * Right now, we are primarily concerned with down-converting of ++ * RAID1 LVs, but parity RAIDs and RAID10 will also have to be ++ * considered. ++ * (e.g. It would not be good to allow extracting a dev from a ++ * stripe set while upconverting to RAID5/6.) ++ */ ++ if (!segtype_is_raid1(seg->segtype)) ++ return 1; ++ ++ /* ++ * We can allow extracting images if the array is performing a ++ * sync operation as long as it is "recover" and the image is not ++ * a primary image or if "resync". ++ */ ++ if (!lv_raid_sync_action(lv, &sync_action) || ++ !lv_raid_dev_health(lv, &dev_health)) ++ return_0; ++ ++ if (!strcmp("idle", sync_action)) { ++ log_error(INTERNAL_ERROR ++ "RAID LV should not be out-of-sync and \"idle\""); ++ return 0; ++ } ++ ++ if (!strcmp("resync", sync_action)) ++ return 1; ++ ++ /* If anything other than "recover" */ ++ if (strcmp("recover", sync_action)) { ++ log_error("Unable to remove RAID image while array" ++ " is performing \"%s\"", sync_action); ++ return 0; ++ } ++ ++ if (seg->area_count != strlen(dev_health)) { ++ log_error(INTERNAL_ERROR ++ "RAID LV area_count differs from number of health characters"); ++ return 0; ++ } ++ ++ for (s = 0; s < seg->area_count; s++) ++ if (dev_health[s] == 'A') ++ redundancy++; ++ ++ for (s = 0; (s < seg->area_count) && extract_count; s++) { ++ if (!lv_is_on_pvs(seg_lv(seg, s), target_pvs) && ++ !lv_is_on_pvs(seg_metalv(seg, s), target_pvs)) ++ continue; ++ if ((dev_health[s] == 'A') && !--redundancy) { ++ log_error("Unable to remove all primary source devices"); ++ return 0; ++ } ++ extract_count--; ++ } ++ return 1; ++} ++ ++/* + * _raid_extract_images + * @lv + * @force: force a replacement in case of primary mirror leg +@@ -2833,6 +2973,10 @@ static int _raid_extract_images(struct logical_volume *lv, + struct segment_type *error_segtype; + + extract = seg->area_count - new_count; ++ ++ if (!_raid_allow_extraction(lv, extract, target_pvs)) ++ return_0; ++ + log_verbose("Extracting %u %s from %s.", extract, + (extract > 1) ? "images" : "image", + display_lvname(lv)); +@@ -2888,20 +3032,8 @@ static int _raid_extract_images(struct logical_volume *lv, + if (!lv_is_on_pvs(seg_lv(seg, s), target_pvs) && + !lv_is_on_pvs(seg_metalv(seg, s), target_pvs)) + continue; +- +- /* +- * Kernel may report raid LV in-sync but still +- * image devices may not be in-sync or faulty. +- */ +- if (!_raid_devs_sync_healthy(lv) && +- (!seg_is_mirrored(seg) || (s == 0 && !force))) { +- log_error("Unable to extract %sRAID image" +- " while RAID array is not in-sync%s.", +- seg_is_mirrored(seg) ? "primary " : "", +- seg_is_mirrored(seg) ? " (use --force option to replace)" : ""); +- return 0; +- } + } ++ + if (!_extract_image_components(seg, s, &rmeta_lv, &rimage_lv)) { + log_error("Failed to extract %s from %s.", + display_lvname(seg_lv(seg, s)), +@@ -3035,6 +3167,13 @@ int lv_raid_change_image_count(struct logical_volume *lv, int yes, uint32_t new_ + const char *level = seg->area_count == 1 ? "raid1 with " : ""; + const char *resil = new_count < seg->area_count ? "reducing" : "enhancing"; + ++ /* LV must be active to perform raid conversion operations */ ++ if (!lv_is_active(lv)) { ++ log_error("%s must be active to perform this operation.", ++ display_lvname(lv)); ++ return 0; ++ } ++ + if (new_count != 1 && /* Already prompted for in _raid_remove_images() */ + !yes && yes_no_prompt("Are you sure you want to convert %s LV %s to %s%u images %s resilience? [y/n]: ", + lvseg_name(first_seg(lv)), display_lvname(lv), level, new_count, resil) == 'n') { +@@ -4437,6 +4576,7 @@ static int _log_possible_conversion(uint64_t *processed_segtypes, void *data) + return 1; + } + ++/* Return any segment type alias name for @segtype or empty string */ + static const char *_get_segtype_alias(const struct segment_type *segtype) + { + if (!strcmp(segtype->name, SEG_TYPE_NAME_RAID5)) +@@ -4460,12 +4600,28 @@ static const char *_get_segtype_alias(const struct segment_type *segtype) + return ""; + } + ++/* Return any segment type alias string (format " (same as raid*)") for @segtype or empty string */ ++static const char *_get_segtype_alias_str(const struct logical_volume *lv, const struct segment_type *segtype) ++{ ++ const char *alias = _get_segtype_alias(segtype); ++ ++ if (*alias) { ++ const char *msg = " (same as "; ++ size_t sz = strlen(msg) + strlen(alias) + 2; ++ char *buf = dm_pool_alloc(lv->vg->cmd->mem, sz); ++ ++ if (buf) ++ alias = (dm_snprintf(buf, sz, "%s%s)", msg, alias) < 0) ? "" : buf; ++ } ++ ++ return alias; ++} ++ + static int _log_possible_conversion_types(const struct logical_volume *lv, const struct segment_type *new_segtype) + { + unsigned possible_conversions = 0; + const struct lv_segment *seg = first_seg(lv); + struct possible_type *pt = NULL; +- const char *alias; + uint64_t processed_segtypes = UINT64_C(0); + + /* Count any possible segment types @seg an be directly converted to */ +@@ -4476,12 +4632,10 @@ static int _log_possible_conversion_types(const struct logical_volume *lv, const + if (!possible_conversions) + log_error("Direct conversion of %s LV %s is not possible.", lvseg_name(seg), display_lvname(lv)); + else { +- alias = _get_segtype_alias(seg->segtype); +- +- log_error("Converting %s from %s%s%s%s is " ++ log_error("Converting %s from %s%s is " + "directly possible to the following layout%s:", + display_lvname(lv), lvseg_name(seg), +- *alias ? " (same as " : "", alias, *alias ? ")" : "", ++ _get_segtype_alias_str(lv, seg->segtype), + possible_conversions > 1 ? "s" : ""); + + pt = NULL; +@@ -4526,10 +4680,16 @@ static int _takeover_noop(TAKEOVER_FN_ARGS) + + static int _takeover_unsupported(TAKEOVER_FN_ARGS) + { +- log_error("Converting the segment type for %s from %s to %s is not supported.", +- display_lvname(lv), lvseg_name(first_seg(lv)), +- (segtype_is_striped_target(new_segtype) && +- (new_stripes == 1)) ? SEG_TYPE_NAME_LINEAR : new_segtype->name); ++ struct lv_segment *seg = first_seg(lv); ++ ++ if (seg->segtype == new_segtype) ++ log_error("Logical volume %s already is type %s.", ++ display_lvname(lv), lvseg_name(seg)); ++ else ++ log_error("Converting the segment type for %s from %s to %s is not supported.", ++ display_lvname(lv), lvseg_name(seg), ++ (segtype_is_striped_target(new_segtype) && ++ (new_stripes == 1)) ? SEG_TYPE_NAME_LINEAR : new_segtype->name); + + if (!_log_possible_conversion_types(lv, new_segtype)) + stack; +@@ -4749,9 +4909,6 @@ static int _rename_area_lvs(struct logical_volume *lv, const char *suffix) + return_0; + } + +- for (s = 0; s < SLV_COUNT; s++) +- dm_pool_free(lv->vg->cmd->mem, sfx[s]); +- + return 1; + } + +@@ -4844,6 +5001,15 @@ static int _raid45_to_raid54_wrapper(TAKEOVER_FN_ARGS) + return 0; + } + ++ if (!yes && yes_no_prompt("Are you sure you want to convert %s%s LV %s to %s%s type? [y/n]: ", ++ lvseg_name(seg), _get_segtype_alias_str(lv, seg->segtype), ++ display_lvname(lv), new_segtype->name, ++ _get_segtype_alias_str(lv, new_segtype)) == 'n') { ++ log_error("Logical volume %s NOT converted to \"%s\".", ++ display_lvname(lv), new_segtype->name); ++ return 0; ++ } ++ + log_debug_metadata("Converting LV %s from %s to %s.", display_lvname(lv), + (seg_is_raid4(seg) ? SEG_TYPE_NAME_RAID4 : SEG_TYPE_NAME_RAID5_N), + (seg_is_raid4(seg) ? SEG_TYPE_NAME_RAID5_N : SEG_TYPE_NAME_RAID4)); +@@ -5018,7 +5184,7 @@ static int _takeover_downconvert_wrapper(TAKEOVER_FN_ARGS) + } + + if (segtype_is_raid4(new_segtype)) +- return _raid45_to_raid54_wrapper(lv, new_segtype, yes, force, first_seg(lv)->area_count, ++ return _raid45_to_raid54_wrapper(lv, new_segtype, 1 /* yes */, force, first_seg(lv)->area_count, + 1 /* data_copies */, 0, 0, 0, allocate_pvs); + + return 1; +@@ -5200,7 +5366,7 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + if (!(raid5_n_segtype = get_segtype_from_flag(lv->vg->cmd, SEG_RAID5_N))) + return_0; + +- /* raid6 upconvert: vonvert to raid5_n preserving already allocated new image component pair */ ++ /* raid6 upconvert: convert to raid5_n preserving already allocated new image component pair */ + if (segtype_is_any_raid6(new_segtype)) { + struct logical_volume *meta_lv, *data_lv; + +@@ -5217,7 +5383,7 @@ static int _takeover_upconvert_wrapper(TAKEOVER_FN_ARGS) + extents_copied, seg_len); + seg->area_count--; + +- if (!_raid45_to_raid54_wrapper(lv, raid5_n_segtype, yes, force, seg->area_count, ++ if (!_raid45_to_raid54_wrapper(lv, raid5_n_segtype, 1 /* yes */, force, seg->area_count, + 1 /* data_copies */, 0, 0, 0, allocate_pvs)) + return 0; + +@@ -5751,94 +5917,172 @@ static uint64_t _r5_to_r6[][2] = { + + + /* Return segment type flag for raid5 -> raid6 conversions */ +-static uint64_t _get_r56_flag(const struct lv_segment *seg, unsigned idx) ++static uint64_t _get_r56_flag(const struct segment_type *segtype, unsigned idx) + { + unsigned elems = ARRAY_SIZE(_r5_to_r6); + + while (elems--) +- if (seg->segtype->flags & _r5_to_r6[elems][idx]) ++ if (segtype->flags & _r5_to_r6[elems][idx]) + return _r5_to_r6[elems][!idx]; + + return 0; + } + +-/* Return segment type flag for raid5 -> raid6 conversions */ ++/* Return segment type flag of @seg for raid5 -> raid6 conversions */ + static uint64_t _raid_seg_flag_5_to_6(const struct lv_segment *seg) + { +- return _get_r56_flag(seg, 0); ++ return _get_r56_flag(seg->segtype, 0); + } + +-/* Return segment type flag for raid6 -> raid5 conversions */ ++/* Return segment type flag of @seg for raid6 -> raid5 conversions */ + static uint64_t _raid_seg_flag_6_to_5(const struct lv_segment *seg) + { +- return _get_r56_flag(seg, 1); ++ return _get_r56_flag(seg->segtype, 1); + } + +-/* Change segtype for raid4 <-> raid5 <-> raid6 where necessary. */ +-static int _set_convenient_raid1456_segtype_to(const struct lv_segment *seg_from, +- const struct segment_type **segtype, +- int yes) ++/* Return segment type flag of @segtype for raid5 -> raid6 conversions */ ++static uint64_t _raid_segtype_flag_5_to_6(const struct segment_type *segtype) + { +- size_t len = min(strlen((*segtype)->name), strlen(lvseg_name(seg_from))); +- uint64_t seg_flag; ++ return _get_r56_flag(segtype, 0); ++} ++ ++/* Change segtype for raid* for convenience where necessary. */ ++/* FIXME: do this like _conversion_options_allowed()? */ ++static int _set_convenient_raid145610_segtype_to(const struct lv_segment *seg_from, ++ const struct segment_type **segtype, ++ int yes) ++{ ++ uint64_t seg_flag = 0; + struct cmd_context *cmd = seg_from->lv->vg->cmd; + const struct segment_type *segtype_sav = *segtype; + + /* Bail out if same RAID level is requested. */ +- if (!strncmp((*segtype)->name, lvseg_name(seg_from), len)) ++ if (is_same_level(seg_from->segtype, *segtype)) + return 1; + +- /* Striped/raid0 -> raid5/6 */ ++ log_debug("Checking LV %s requested %s segment type for convenience", ++ display_lvname(seg_from->lv), (*segtype)->name); ++ ++ /* striped/raid0 -> raid5/6 */ + if (seg_is_striped(seg_from) || seg_is_any_raid0(seg_from)) { + /* If this is any raid5 conversion request -> enforce raid5_n, because we convert from striped */ +- if (segtype_is_any_raid5(*segtype) && !segtype_is_raid5_n(*segtype)) { ++ if (segtype_is_any_raid5(*segtype) && !segtype_is_raid5_n(*segtype)) + seg_flag = SEG_RAID5_N; +- goto replaced; + + /* If this is any raid6 conversion request -> enforce raid6_n_6, because we convert from striped */ +- } else if (segtype_is_any_raid6(*segtype) && !segtype_is_raid6_n_6(*segtype)) { ++ else if (segtype_is_any_raid6(*segtype) && !segtype_is_raid6_n_6(*segtype)) + seg_flag = SEG_RAID6_N_6; +- goto replaced; ++ ++ /* raid1 -> */ ++ } else if (seg_is_raid1(seg_from) && !segtype_is_mirror(*segtype)) { ++ if (seg_from->area_count != 2) { ++ log_warn("Convert %s LV %s to 2 images first.", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ return 0; ++ ++ } else if (segtype_is_striped(*segtype) || ++ segtype_is_any_raid0(*segtype) || ++ segtype_is_raid10(*segtype)) ++ seg_flag = SEG_RAID5_N; ++ ++ else if (!segtype_is_raid4(*segtype) && !segtype_is_any_raid5(*segtype)) ++ seg_flag = SEG_RAID5_LS; ++ ++ /* raid4/raid5 -> striped/raid0/raid1/raid6/raid10 */ ++ } else if (seg_is_raid4(seg_from) || seg_is_any_raid5(seg_from)) { ++ if (segtype_is_raid1(*segtype) && ++ seg_from->area_count != 2) { ++ log_warn("Convert %s LV %s to 2 stripes first (i.e. --stripes 1).", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ return 0; ++ ++ } else if (seg_is_raid4(seg_from) && ++ segtype_is_any_raid5(*segtype) && ++ !segtype_is_raid5_n(*segtype)) ++ seg_flag = SEG_RAID5_N; ++ ++ else if (seg_is_any_raid5(seg_from) && ++ segtype_is_raid4(*segtype) && ++ !segtype_is_raid5_n(*segtype)) ++ seg_flag = SEG_RAID5_N; ++ ++ else if (segtype_is_raid10(*segtype)) { ++ if (seg_from->area_count < 3) { ++ log_warn("Convert %s LV %s to minimum 3 stripes first (i.e. --stripes 2).", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ return 0; ++ } ++ ++ seg_flag = SEG_RAID0_META; ++ ++ } else if (segtype_is_any_raid6(*segtype)) { ++ if (seg_from->area_count < 4) { ++ log_warn("Convert %s LV %s to minimum 4 stripes first (i.e. --stripes 3).", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ return 0; ++ ++ } else if (seg_is_raid4(seg_from) && !segtype_is_raid6_n_6(*segtype)) ++ seg_flag = SEG_RAID6_N_6; ++ else ++ seg_flag = _raid_seg_flag_5_to_6(seg_from); + } + +- /* raid4 -> raid5_n */ +- } else if (seg_is_raid4(seg_from) && segtype_is_any_raid5(*segtype)) { +- seg_flag = SEG_RAID5_N; +- goto replaced; ++ /* raid6 -> striped/raid0/raid5/raid10 */ ++ } else if (seg_is_any_raid6(seg_from)) { ++ if (segtype_is_raid1(*segtype)) { ++ /* No result for raid6_{zr,nr,nc} */ ++ if (!(seg_flag = _raid_seg_flag_6_to_5(seg_from)) || ++ !(seg_flag & (*segtype)->flags)) ++ seg_flag = SEG_RAID6_LS_6; + +- /* raid4/raid5_n -> striped/raid0/raid6 */ +- } else if ((seg_is_raid4(seg_from) || seg_is_raid5_n(seg_from)) && +- !segtype_is_striped(*segtype) && +- !segtype_is_any_raid0(*segtype) && +- !segtype_is_raid1(*segtype) && +- !segtype_is_raid4(*segtype) && +- !segtype_is_raid5_n(*segtype) && +- !segtype_is_raid6_n_6(*segtype)) { +- seg_flag = SEG_RAID6_N_6; +- goto replaced; +- +- /* Got to do check for raid5 -> raid6 ... */ +- } else if (seg_is_any_raid5(seg_from) && segtype_is_any_raid6(*segtype)) { +- if (!(seg_flag = _raid_seg_flag_5_to_6(seg_from))) +- return_0; +- goto replaced; ++ } else if (segtype_is_any_raid10(*segtype)) { ++ seg_flag = seg_is_raid6_n_6(seg_from) ? SEG_RAID0_META : SEG_RAID6_N_6; ++ ++ } else if ((segtype_is_striped(*segtype) || segtype_is_any_raid0(*segtype)) && ++ !seg_is_raid6_n_6(seg_from)) { ++ seg_flag = SEG_RAID6_N_6; ++ ++ } else if (segtype_is_raid4(*segtype) && !seg_is_raid6_n_6(seg_from)) { ++ seg_flag = SEG_RAID6_N_6; + +- /* ... and raid6 -> raid5 */ +- } else if (seg_is_any_raid6(seg_from) && segtype_is_any_raid5(*segtype)) { +- /* No result for raid6_{zr,nr,nc} */ +- if (!(seg_flag = _raid_seg_flag_6_to_5(seg_from))) ++ } else if (segtype_is_any_raid5(*segtype)) ++ /* No result for raid6_{zr,nr,nc} */ ++ if (!(seg_flag = _raid_seg_flag_6_to_5(seg_from)) || ++ !(seg_flag & (*segtype)->flags)) ++ seg_flag = _raid_segtype_flag_5_to_6(*segtype); ++ ++ /* -> raid1 */ ++ } else if (!seg_is_mirror(seg_from) && segtype_is_raid1(*segtype)) { ++ if (!seg_is_raid4(seg_from) && !seg_is_any_raid5(seg_from)) { ++ log_warn("Convert %s LV %s to raid4/raid5 first.", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); + return 0; +- goto replaced; +- } + +- return 1; ++ } else if (seg_from->area_count != 2) { ++ log_warn("Convert %s LV %s to 2 stripes first (i.e. --stripes 1).", ++ lvseg_name(seg_from), display_lvname(seg_from->lv)); ++ return 0; ++ ++ } ++ ++ /* raid10 -> ... */ ++ } else if (seg_is_raid10(seg_from) && ++ !segtype_is_striped(*segtype) && ++ !segtype_is_any_raid0(*segtype)) ++ seg_flag = SEG_RAID0_META; ++ ++ if (seg_flag) { ++ if (!(*segtype = get_segtype_from_flag(cmd, seg_flag))) ++ return_0; ++ if (segtype_sav != *segtype) { ++ log_warn("Replaced LV type %s%s with possible type %s.", ++ segtype_sav->name, _get_segtype_alias_str(seg_from->lv, segtype_sav), ++ (*segtype)->name); ++ log_warn("Repeat this command to convert to %s after an interim conversion has finished.", ++ segtype_sav->name); ++ } ++ } + +-replaced: +- if (!(*segtype = get_segtype_from_flag(cmd, seg_flag))) +- return_0; +- if (segtype_sav != *segtype) +- log_warn("Replaced LV type %s with possible type %s.", +- segtype_sav->name, (*segtype)->name); + return 1; + } + +@@ -5905,6 +6149,8 @@ static int _region_size_change_requested(struct logical_volume *lv, int yes, con + return 0; + } + ++ lv->status &= ~LV_RESHAPE; ++ + if (!lv_update_and_reload_origin(lv)) + return_0; + +@@ -5924,7 +6170,7 @@ static int _conversion_options_allowed(const struct lv_segment *seg_from, + int r = 1; + uint32_t opts; + +- if (!new_image_count && !_set_convenient_raid1456_segtype_to(seg_from, segtype_to, yes)) ++ if (!new_image_count && !_set_convenient_raid145610_segtype_to(seg_from, segtype_to, yes)) + return_0; + + if (!_get_allowed_conversion_options(seg_from, *segtype_to, new_image_count, &opts)) { +@@ -5952,12 +6198,28 @@ static int _conversion_options_allowed(const struct lv_segment *seg_from, + } + + if (r && ++ !yes && + strcmp((*segtype_to)->name, SEG_TYPE_NAME_MIRROR) && /* "mirror" is prompted for later */ +- !yes && yes_no_prompt("Are you sure you want to convert %s LV %s to %s type? [y/n]: ", +- lvseg_name(seg_from), display_lvname(seg_from->lv), ++ !is_same_level(seg_from->segtype, *segtype_to)) { /* Prompt here for takeover */ ++ const char *basic_fmt = "Are you sure you want to convert %s LV %s"; ++ const char *type_fmt = " to %s type"; ++ const char *question_fmt = "? [y/n]: "; ++ char *fmt; ++ size_t sz = strlen(basic_fmt) + ((seg_from->segtype == *segtype_to) ? 0 : strlen(type_fmt)) + strlen(question_fmt) + 1; ++ ++ if (!(fmt = dm_pool_alloc(seg_from->lv->vg->cmd->mem, sz))) ++ return_0; ++ ++ if (dm_snprintf(fmt, sz, "%s%s%s", basic_fmt, (seg_from->segtype == *segtype_to) ? "" : type_fmt, question_fmt) < 0) { ++ log_error(INTERNAL_ERROR "dm_snprintf failed."); ++ return_0; ++ } ++ ++ if (yes_no_prompt(fmt, lvseg_name(seg_from), display_lvname(seg_from->lv), + (*segtype_to)->name) == 'n') { +- log_error("Logical volume %s NOT converted.", display_lvname(seg_from->lv)); +- r = 0; ++ log_error("Logical volume %s NOT converted.", display_lvname(seg_from->lv)); ++ r = 0; ++ } + } + + return r; +@@ -6014,6 +6276,15 @@ int lv_raid_convert(struct logical_volume *lv, + uint32_t available_slvs, removed_slvs; + takeover_fn_t takeover_fn; + ++ /* FIXME If not active, prompt and activate */ ++ /* FIXME Some operations do not require the LV to be active */ ++ /* LV must be active to perform raid conversion operations */ ++ if (!lv_is_active(lv)) { ++ log_error("%s must be active to perform this operation.", ++ display_lvname(lv)); ++ return 0; ++ } ++ + new_segtype = new_segtype ? : seg->segtype; + if (!new_segtype) { + log_error(INTERNAL_ERROR "New segtype not specified."); +@@ -6040,6 +6311,15 @@ int lv_raid_convert(struct logical_volume *lv, + region_size = region_size ? : get_default_region_size(lv->vg->cmd); + + /* ++ * Check acceptible options mirrors, region_size, ++ * stripes and/or stripe_size have been provided. ++ */ ++ if (!_conversion_options_allowed(seg, &new_segtype, yes, ++ 0 /* Takeover */, 0 /*new_data_copies*/, new_region_size, ++ new_stripes, new_stripe_size_supplied)) ++ return _log_possible_conversion_types(lv, new_segtype); ++ ++ /* + * reshape of capable raid type requested + */ + switch (_reshape_requested(lv, new_segtype, data_copies, region_size, stripes, stripe_size)) { +@@ -6074,15 +6354,6 @@ int lv_raid_convert(struct logical_volume *lv, + return 0; + } + +- /* +- * Check acceptible options mirrors, region_size, +- * stripes and/or stripe_size have been provided. +- */ +- if (!_conversion_options_allowed(seg, &new_segtype, yes, +- 0 /* Takeover */, 0 /*new_data_copies*/, new_region_size, +- new_stripes, new_stripe_size_supplied)) +- return _log_possible_conversion_types(lv, new_segtype); +- + takeover_fn = _get_takeover_fn(first_seg(lv), new_segtype, new_image_count); + + /* Exit without doing activation checks if the combination isn't possible */ +@@ -6117,15 +6388,6 @@ int lv_raid_convert(struct logical_volume *lv, + (segtype_is_striped_target(new_segtype) && + (new_stripes == 1)) ? SEG_TYPE_NAME_LINEAR : new_segtype->name); + +- /* FIXME If not active, prompt and activate */ +- /* FIXME Some operations do not require the LV to be active */ +- /* LV must be active to perform raid conversion operations */ +- if (!lv_is_active(lv)) { +- log_error("%s must be active to perform this operation.", +- display_lvname(lv)); +- return 0; +- } +- + /* In clustered VGs, the LV must be active on this node exclusively. */ + if (vg_is_clustered(lv->vg) && !lv_is_active_exclusive_locally(lv)) { + log_error("%s must be active exclusive locally to " +@@ -6140,6 +6402,8 @@ int lv_raid_convert(struct logical_volume *lv, + return 0; + } + ++ lv->status &= ~LV_RESHAPE; ++ + return takeover_fn(lv, new_segtype, yes, force, new_image_count, 0, new_stripes, stripe_size, + region_size, allocate_pvs); + } +@@ -6229,6 +6493,39 @@ has_enough_space: + } + + /* ++ * _lv_raid_has_primary_failure_on_recover ++ * @lv ++ * ++ * The kernel behaves strangely in the presense of a primary failure ++ * during a "recover" sync operation. It's not technically a bug, I ++ * suppose, but the output of the status line can make it difficult ++ * to determine that we are in this state. The sync ratio will be ++ * 100% and the sync action will be "idle", but the health characters ++ * will be e.g. "Aaa" or "Aa", where the 'A' is the dead ++ * primary source that cannot be marked dead by the kernel b/c ++ * it is the only source for the remainder of data. ++ * ++ * This function helps to detect that condition. ++ * ++ * Returns: 1 if the state is detected, 0 otherwise. ++ * FIXME: would be better to return -1,0,1 to allow error report. ++ */ ++int _lv_raid_has_primary_failure_on_recover(struct logical_volume *lv) ++{ ++ char *tmp_dev_health; ++ char *tmp_sync_action; ++ ++ if (!lv_raid_sync_action(lv, &tmp_sync_action) || ++ !lv_raid_dev_health(lv, &tmp_dev_health)) ++ return_0; ++ ++ if (!strcmp(tmp_sync_action, "idle") && strchr(tmp_dev_health, 'a')) ++ return 1; ++ ++ return 0; ++} ++ ++/* + * Helper: + * + * _lv_raid_rebuild_or_replace +@@ -6279,11 +6576,38 @@ static int _lv_raid_rebuild_or_replace(struct logical_volume *lv, + } + + if (!_raid_in_sync(lv)) { ++ /* ++ * FIXME: There is a bug in the kernel that prevents 'rebuild' ++ * from being specified when the array is not in-sync. ++ * There are conditions where this should be allowed, ++ * but only when we are doing a repair - as indicated by ++ * 'lv->vg->cmd->handles_missing_pvs'. The above ++ * conditional should be: ++ (!lv->vg->cmd->handles_missing_pvs && !_raid_in_sync(lv)) ++ */ + log_error("Unable to replace devices in %s while it is " + "not in-sync.", display_lvname(lv)); + return 0; + } + ++ if (_lv_raid_has_primary_failure_on_recover(lv)) { ++ /* ++ * I hate having multiple error lines, but this ++ * seems to work best for syslog and CLI. ++ */ ++ log_error("Unable to repair %s/%s. Source devices failed" ++ " before the RAID could synchronize.", ++ lv->vg->name, lv->name); ++ log_error("You should choose one of the following:"); ++ log_error(" 1) deactivate %s/%s, revive failed " ++ "device, re-activate LV, and proceed.", ++ lv->vg->name, lv->name); ++ log_error(" 2) remove the LV (all data is lost)."); ++ log_error(" 3) Seek expert advice to attempt to salvage any" ++ " data from remaining devices."); ++ return 0; ++ } ++ + /* + * How many sub-LVs are being removed? + */ +diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h +index 93132c3..2acb894 100644 +--- a/lib/metadata/segtype.h ++++ b/lib/metadata/segtype.h +@@ -290,6 +290,24 @@ struct segment_type *init_unknown_segtype(struct cmd_context *cmd, + #define RAID_FEATURE_RAID4 (1U << 3) /* ! version 1.8 or 1.9.0 */ + #define RAID_FEATURE_SHRINK (1U << 4) /* version 1.9.0 */ + #define RAID_FEATURE_RESHAPE (1U << 5) /* version 1.10.1 */ ++/* ++ * RAID_FEATURE_NEW_DEVICES_ACCEPT_REBUILD ++ * This signifies a behavioral change in dm-raid. Prior to upstream kernel ++ * commit 33e53f068, the kernel would refuse to allow 'rebuild' CTR args to ++ * be submitted when other devices in the array had uninitialized superblocks. ++ * After the commit, these parameters were allowed. ++ * ++ * The most obvious useful case of this new behavior is up-converting a ++ * linear device to RAID1. A new superblock is allocated for the linear dev ++ * and it will be uninitialized, while all the new images are specified for ++ * 'rebuild'. This valid scenario would not have been allowed prior to ++ * commit 33e53f068. ++ * ++ * Commit 33e53f068 did not bump the dm-raid version number. So it exists ++ * in some, but not all 1.8.1 versions of dm-raid. The only way to be ++ * certain the new behavior exists is to check for version 1.9.0. ++ */ ++#define RAID_FEATURE_NEW_DEVICES_ACCEPT_REBUILD (1U << 6) /* version 1.9.0 */ + + #ifdef RAID_INTERNAL + int init_raid_segtypes(struct cmd_context *cmd, struct segtype_library *seglib); +diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c +index f9cd3d0..ad45ce9 100644 +--- a/lib/metadata/thin_manip.c ++++ b/lib/metadata/thin_manip.c +@@ -563,6 +563,12 @@ static uint64_t _estimate_metadata_size(uint32_t data_extents, uint32_t extent_s + return _estimate_size(data_extents, extent_size, chunk_size); + } + ++/* Estimate maximal supportable thin pool data size for given chunk_size */ ++static uint64_t _estimate_max_data_size(uint32_t chunk_size) ++{ ++ return chunk_size * (DEFAULT_THIN_POOL_MAX_METADATA_SIZE * 2) * SECTOR_SIZE / UINT64_C(64); ++} ++ + /* Estimate thin pool chunk size from data and metadata size (in sector units) */ + static uint32_t _estimate_chunk_size(uint32_t data_extents, uint32_t extent_size, + uint64_t metadata_size, int attr) +@@ -628,6 +634,7 @@ int update_thin_pool_params(struct cmd_context *cmd, + { + uint64_t pool_metadata_size = (uint64_t) *pool_metadata_extents * extent_size; + uint32_t estimate_chunk_size; ++ uint64_t max_pool_data_size; + const char *str; + + if (!*chunk_size && +@@ -666,7 +673,7 @@ int update_thin_pool_params(struct cmd_context *cmd, + + /* Check if we should eventually use bigger chunk size */ + while ((pool_metadata_size > +- (DEFAULT_THIN_POOL_OPTIMAL_SIZE / SECTOR_SIZE)) && ++ (DEFAULT_THIN_POOL_OPTIMAL_METADATA_SIZE * 2)) && + (*chunk_size < DM_THIN_MAX_DATA_BLOCK_SIZE)) { + *chunk_size <<= 1; + pool_metadata_size >>= 1; +@@ -704,6 +711,16 @@ int update_thin_pool_params(struct cmd_context *cmd, + } + } + ++ max_pool_data_size = _estimate_max_data_size(*chunk_size); ++ if ((max_pool_data_size / extent_size) < pool_data_extents) { ++ log_error("Selected chunk size %s cannot address more then %s of thin pool data space.", ++ display_size(cmd, *chunk_size), display_size(cmd, max_pool_data_size)); ++ return 0; ++ } ++ ++ log_print_unless_silent("Thin pool volume with chunk size %s can address at most %s of data.", ++ display_size(cmd, *chunk_size), display_size(cmd, max_pool_data_size)); ++ + if (!validate_thin_pool_chunk_size(cmd, *chunk_size)) + return_0; + +diff --git a/lib/raid/raid.c b/lib/raid/raid.c +index 25009f6..8a53d7e 100644 +--- a/lib/raid/raid.c ++++ b/lib/raid/raid.c +@@ -474,6 +474,7 @@ static int _raid_target_present(struct cmd_context *cmd, + { 1, 3, 0, RAID_FEATURE_RAID10, SEG_TYPE_NAME_RAID10 }, + { 1, 7, 0, RAID_FEATURE_RAID0, SEG_TYPE_NAME_RAID0 }, + { 1, 9, 0, RAID_FEATURE_SHRINK, "shrinking" }, ++ { 1, 9, 0, RAID_FEATURE_NEW_DEVICES_ACCEPT_REBUILD, "rebuild+emptymeta" }, + { 1, 10, 1, RAID_FEATURE_RESHAPE, "reshaping" }, + }; + +diff --git a/libdm/libdm-stats.c b/libdm/libdm-stats.c +index 6ce6c57..b0c9e4b 100644 +--- a/libdm/libdm-stats.c ++++ b/libdm/libdm-stats.c +@@ -4466,6 +4466,7 @@ static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd, + return extents; + + bad: ++ *count = 0; + dm_pool_abandon_object(mem); + dm_free(buf); + return NULL; +@@ -4536,7 +4537,7 @@ static int _stats_unmap_regions(struct dm_stats *dms, uint64_t group_id, + region = &dms->regions[i]; + nr_old++; + +- if (_find_extent(*count, extents, ++ if (extents && _find_extent(*count, extents, + region->start, region->len)) { + ext.start = region->start; + ext.len = region->len; +@@ -4653,11 +4654,12 @@ static uint64_t *_stats_map_file_regions(struct dm_stats *dms, int fd, + * causing complications in the error path. + */ + if (!(extent_mem = dm_pool_create("extents", sizeof(*extents)))) +- return_0; ++ return_NULL; + + if (!(extents = _stats_get_extents_for_file(extent_mem, fd, count))) { +- dm_pool_destroy(extent_mem); +- return_0; ++ log_very_verbose("No extents found in fd %d", fd); ++ if (!update) ++ goto out; + } + + if (update) { +@@ -4734,7 +4736,10 @@ static uint64_t *_stats_map_file_regions(struct dm_stats *dms, int fd, + if (bounds) + dm_free(hist_arg); + +- dm_pool_free(extent_mem, extents); ++ /* the extent table will be empty if the file has been truncated. */ ++ if (extents) ++ dm_pool_free(extent_mem, extents); ++ + dm_pool_destroy(extent_mem); + + return regions; +@@ -4755,12 +4760,6 @@ out_remove: + *count = 0; + + out: +- /* +- * The table of file extents in 'extents' is always built, so free +- * it explicitly: this will also free any 'old_extents' table that +- * was later allocated from the 'extent_mem' pool by this function. +- */ +- dm_pool_free(extent_mem, extents); + dm_pool_destroy(extent_mem); + dm_free(hist_arg); + dm_free(regions); +@@ -4872,7 +4871,8 @@ uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd, + if (!dm_stats_list(dms, NULL)) + goto bad; + +- if (regroup) ++ /* regroup if there are regions to group */ ++ if (regroup && (*regions != DM_STATS_REGION_NOT_PRESENT)) + if (!_stats_group_file_regions(dms, regions, count, alias)) + goto bad; + +diff --git a/liblvm/lvm_lv.c b/liblvm/lvm_lv.c +index d5ca821..13d6cbf 100644 +--- a/liblvm/lvm_lv.c ++++ b/liblvm/lvm_lv.c +@@ -507,7 +507,7 @@ static int _lv_set_pool_params(struct lvcreate_params *lp, + pool_metadata_size = extents * vg->extent_size / + (lp->chunk_size * (SECTOR_SIZE / 64)); + while ((pool_metadata_size > +- (2 * DEFAULT_THIN_POOL_OPTIMAL_SIZE / SECTOR_SIZE)) && ++ (DEFAULT_THIN_POOL_OPTIMAL_METADATA_SIZE * 2)) && + lp->chunk_size < DM_THIN_MAX_DATA_BLOCK_SIZE) { + lp->chunk_size <<= 1; + pool_metadata_size >>= 1; +diff --git a/man/clvmd.8_main b/man/clvmd.8_main +index 8e9921d..de6ce25 100644 +--- a/man/clvmd.8_main ++++ b/man/clvmd.8_main +@@ -31,6 +31,9 @@ clvmd \(em cluster LVM daemon + clvmd is the daemon that distributes LVM metadata updates around a cluster. + It must be running on all nodes in the cluster and will give an error + if a node in the cluster does not have this daemon running. ++ ++Also see \fBlvmlockd\fP(8) for a newer method of using LVM on shared ++storage. + . + .SH OPTIONS + . +@@ -196,4 +199,6 @@ Defaults to \fI#LVM_PATH#\fP. + .SH SEE ALSO + .BR syslog (3), + .BR lvm.conf (5), +-.BR lvm (8) ++.BR lvm (8), ++.BR lvmlockd (8), ++.BR lvmsystemid (7) +diff --git a/man/lvm-fullreport.8_des b/man/lvm-fullreport.8_des +index f350a0a..741cd12 100644 +--- a/man/lvm-fullreport.8_des ++++ b/man/lvm-fullreport.8_des +@@ -3,4 +3,3 @@ and LV segments. The information is all gathered together for each VG + (under a per-VG lock) so it is consistent. Information gathered from + separate calls to \fBvgs\fP, \fBpvs\fP, and \fBlvs\fP can be inconsistent + if information changes between commands. +- +diff --git a/man/lvm.8_main b/man/lvm.8_main +index 7506eaf..bd5d8a7 100644 +--- a/man/lvm.8_main ++++ b/man/lvm.8_main +@@ -537,6 +537,8 @@ directly. + .BR lvs (8) + .BR lvscan (8) + ++.BR lvm-fullreport (8) ++.BR lvm-lvpoll (8) + .BR lvm2-activation-generator (8) + .BR blkdeactivate (8) + .BR lvmdump (8) +diff --git a/man/lvm.conf.5_main b/man/lvm.conf.5_main +index 7b777af..3a45f1c 100644 +--- a/man/lvm.conf.5_main ++++ b/man/lvm.conf.5_main +@@ -10,6 +10,10 @@ being loaded - settings read in later override earlier + settings. File timestamps are checked between commands and if + any have changed, all the files are reloaded. + ++For a description of each lvm.conf setting, run: ++ ++.B lvmconfig --typeconfig default --withcomments --withspaces ++ + The settings defined in lvm.conf can be overridden by any + of these extended configuration methods: + .TP +diff --git a/man/lvmcache.7_main b/man/lvmcache.7_main +index e573b58..1f29185 100644 +--- a/man/lvmcache.7_main ++++ b/man/lvmcache.7_main +@@ -404,6 +404,24 @@ This is equivalent to: + .B lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV + + ++.SS Cache metadata formats ++ ++\& ++ ++There are two disk formats for cache metadata. The metadata format can be ++specified when a cache pool is created, and cannot be changed. ++Format \fB2\fP has better performance; it is more compact, and stores ++dirty bits in a separate btree, which improves the speed of shutting down ++the cache. ++With \fBauto\fP, lvm selects the best option provided by the current ++dm-cache kernel target. ++ ++.B lvconvert --type cache-pool --cachemetadataformat auto|1|2 ++.RS ++.B VG/CacheDataLV ++.RE ++ ++ + .SH SEE ALSO + .BR lvm.conf (5), + .BR lvchange (8), +diff --git a/man/lvmetad.8_main b/man/lvmetad.8_main +index 31a89cc..ec55171 100644 +--- a/man/lvmetad.8_main ++++ b/man/lvmetad.8_main +@@ -53,10 +53,13 @@ metadata. + In some cases, lvmetad will be temporarily disabled while it continues + running. In this state, LVM commands will ignore the lvmetad cache and + revert to scanning disks. A warning will also be printed which includes +-the reason why lvmetad is not being used. The most common reason is the +-existence of duplicate PVs (lvmetad cannot cache data for duplicate PVs.) +-Once duplicates have been resolved, the lvmetad cache is can be updated +-with pvscan --cache and commands will return to using the cache. ++the reason why lvmetad is not being used. The most common reasons are the ++existence of duplicate PVs (lvmetad cannot cache data for duplicate PVs), ++or an 'lvconvert --repair' command has been run (the lvmetad cache may ++not be reliable while repairs are neeeded.) ++Once duplicates have been resolved, or repairs have been completed, ++the lvmetad cache is can be updated with pvscan --cache and commands ++will return to using the cache. + + Use of lvmetad is enabled/disabled by: + .br +diff --git a/man/lvmlockd.8_main b/man/lvmlockd.8_main +index 1a1c2fc..552eb94 100644 +--- a/man/lvmlockd.8_main ++++ b/man/lvmlockd.8_main +@@ -117,17 +117,22 @@ Assign each host a unique host_id in the range 1-2000 by setting + + .SS 3. start lvmlockd + +-Use a service/init file if available, or just run "lvmlockd". ++Use a unit/init file, or run the lvmlockd daemon directly: ++.br ++systemctl start lvm2-lvmlockd + + .SS 4. start lock manager + + .I sanlock + .br ++Use unit/init files, or start wdmd and sanlock daemons directly: ++.br + systemctl start wdmd sanlock + + .I dlm + .br +-Follow external clustering documentation when applicable, otherwise: ++Follow external clustering documentation when applicable, or use ++unit/init files: + .br + systemctl start corosync dlm + +@@ -146,8 +151,8 @@ vgchange --lock-start + lvmlockd requires shared VGs to be started before they are used. This is + a lock manager operation to start (join) the VG lockspace, and it may take + some time. Until the start completes, locks for the VG are not available. +-LVM commands are allowed to read the VG while start is in progress. (An +-init/unit file can also be used to start VGs.) ++LVM commands are allowed to read the VG while start is in progress. (A ++unit/init file can also be used to start VGs.) + + .SS 7. create and activate LVs + +@@ -247,9 +252,9 @@ clvmd for clustering. See below for converting a clvm VG to a lockd VG. + .SS lockd VGs from hosts not using lvmlockd + + Only hosts that use lockd VGs should be configured to run lvmlockd. +-However, shared devices used by lockd VGs may be visible from hosts not +-using lvmlockd. From a host not using lvmlockd, visible lockd VGs are +-ignored in the same way as foreign VGs (see ++However, shared devices in lockd VGs may be visible from hosts not ++using lvmlockd. From a host not using lvmlockd, lockd VGs are ignored ++in the same way as foreign VGs (see + .BR lvmsystemid (7).) + + The --shared option for reporting and display commands causes lockd VGs +@@ -267,9 +272,9 @@ for all vgcreate options. + .B vgcreate + + .IP \[bu] 2 +-Creates a local VG with the local system ID when neither lvmlockd nor clvm are configured. ++Creates a local VG with the local host's system ID when neither lvmlockd nor clvm are configured. + .IP \[bu] 2 +-Creates a local VG with the local system ID when lvmlockd is configured. ++Creates a local VG with the local host's system ID when lvmlockd is configured. + .IP \[bu] 2 + Creates a clvm VG when clvm is configured. + +@@ -300,10 +305,11 @@ LVM commands request locks from clvmd to use the VG. + + .SS creating the first sanlock VG + +-Creating the first sanlock VG is not protected by locking and requires +-special attention. This is because sanlock locks exist within the VG, so +-they are not available until the VG exists. The first sanlock VG will +-contain the "global lock". ++Creating the first sanlock VG is not protected by locking, so it requires ++special attention. This is because sanlock locks exist on storage within ++the VG, so they are not available until the VG exists. The first sanlock ++VG created will automatically contain the "global lock". Be aware of the ++following special considerations: + + .IP \[bu] 2 + The first vgcreate command needs to be given the path to a device that has +@@ -313,6 +319,11 @@ global lock, which will not be available until after the first sanlock VG + is created. + + .IP \[bu] 2 ++Because the first sanlock VG will contain the global lock, this VG needs ++to be accessible to all hosts that will use sanlock shared VGs. All hosts ++will need to use the global lock from the first sanlock VG. ++ ++.IP \[bu] 2 + While running vgcreate for the first sanlock VG, ensure that the device + being used is not used by another LVM command. Allocation of shared + devices is usually protected by the global lock, but this cannot be done +@@ -323,11 +334,6 @@ While running vgcreate for the first sanlock VG, ensure that the VG name + being used is not used by another LVM command. Uniqueness of VG names is + usually ensured by the global lock. + +-.IP \[bu] 2 +-Because the first sanlock VG will contain the global lock, this VG needs +-to be accessible to all hosts that will use sanlock shared VGs. All hosts +-will need to use the global lock from the first sanlock VG. +- + See below for more information about managing the sanlock global lock. + + +@@ -383,7 +389,7 @@ lvmlockd is running + the lock manager is running + .br + \[bu] +-the VG is visible to the system ++the VG's devices are visible on the system + .br + + A lockd VG can be stopped if all LVs are deactivated. +@@ -425,22 +431,23 @@ activation { + + .SS automatic starting and automatic activation + +-Scripts or programs on a host that automatically start VGs will use the +-"auto" option to indicate that the command is being run automatically by +-the system: ++When system-level scripts/programs automatically start VGs, they should ++use the "auto" option. This option indicates that the command is being ++run automatically by the system: + + vgchange --lock-start --lock-opt auto [ ...] + +-Without any additional configuration, including the "auto" option has no +-effect; all VGs are started unless restricted by lock_start_list. ++The "auto" option causes the command to follow the lvm.conf ++activation/auto_lock_start_list. If auto_lock_start_list is undefined, ++all VGs are started, just as if the auto option was not used. + +-However, when the lvm.conf activation/auto_lock_start_list is defined, the +-auto start command performs an additional filtering phase to all VGs being +-started, testing each VG name against the auto_lock_start_list. The +-auto_lock_start_list defines lockd VGs that will be started by the auto +-start command. Visible lockd VGs not included in the list are ignored by +-the auto start command. If the list is undefined, all VG names pass this +-filter. (The lock_start_list is also still used to filter all VGs.) ++When auto_lock_start_list is defined, it lists the lockd VGs that should ++be started by the auto command. VG names that do not match an item in the ++list will be ignored by the auto start command. ++ ++(The lock_start_list is also still used to filter VG names from all start ++commands, i.e. with or without the auto option. When the lock_start_list ++is defined, only VGs matching a list item can be started with vgchange.) + + The auto_lock_start_list allows a user to select certain lockd VGs that + should be automatically started by the system (or indirectly, those that +@@ -470,14 +477,12 @@ The set of orphan PVs and unused devices. + The properties of orphan PVs, e.g. PV size. + .br + +-The global lock is used in shared mode by commands that read this +-information, or in exclusive mode by commands that change it. +- +-The command 'vgs' acquires the global lock in shared mode because it +-reports the list of all VG names. +- +-The vgcreate command acquires the global lock in exclusive mode because it +-creates a new VG name, and it takes a PV from the list of unused PVs. ++The global lock is acquired in shared mode by commands that read this ++information, or in exclusive mode by commands that change it. For ++example, the command 'vgs' acquires the global lock in shared mode because ++it reports the list of all VG names, and the vgcreate command acquires the ++global lock in exclusive mode because it creates a new VG name, and it ++takes a PV from the list of unused PVs. + + When an LVM command is given a tag argument, or uses select, it must read + all VGs to match the tag or selection, which causes the global lock to be +@@ -485,10 +490,10 @@ acquired. + + .I VG lock + +-A VG lock is associated with each VG. The VG lock is acquired in shared +-mode to read the VG and in exclusive mode to change the VG (modify the VG +-metadata or activate LVs). This lock serializes access to a VG with all +-other LVM commands accessing the VG from all hosts. ++A VG lock is associated with each lockd VG. The VG lock is acquired in ++shared mode to read the VG and in exclusive mode to change the VG (modify ++the VG metadata or activating LVs). This lock serializes access to a VG ++with all other LVM commands accessing the VG from all hosts. + + The command 'vgs' will not only acquire the GL lock to read the list of + all VG names, but will acquire the VG lock for each VG prior to reading +@@ -502,7 +507,7 @@ argument. + + An LV lock is acquired before the LV is activated, and is released after + the LV is deactivated. If the LV lock cannot be acquired, the LV is not +-activated. LV locks are persistent and remain in place after the ++activated. LV locks are persistent and remain in place when the + activation command is done. GL and VG locks are transient, and are held + only while an LVM command is running. + +@@ -822,8 +827,8 @@ While lvmlockd and clvmd are entirely different systems, LVM command usage + remains similar. Differences are more notable when using lvmlockd's + sanlock option. + +-Visible usage differences between lockd VGs with lvmlockd and clvm VGs +-with clvmd: ++Visible usage differences between lockd VGs (using lvmlockd) and clvm VGs ++(using clvmd): + + .IP \[bu] 2 + lvm.conf must be configured to use either lvmlockd (use_lvmlockd=1) or +diff --git a/man/lvmraid.7_main b/man/lvmraid.7_main +index f0d28f5..c27f1fa 100644 +--- a/man/lvmraid.7_main ++++ b/man/lvmraid.7_main +@@ -896,7 +896,7 @@ between linear and raid1. + .IP \(bu 3 + between mirror and raid1. + .IP \(bu 3 +-between 2-legged raid1 and raid4/5. ++between raid1 with two images and raid4/5. + .IP \(bu 3 + between striped/raid0 and raid4. + .IP \(bu 3 +@@ -912,39 +912,90 @@ between striped/raid0 and raid10. + .IP \(bu 3 + between striped and raid4. + +-.SS Examples ++.SS Indirect conversions + +-1. Converting an LV from \fBlinear\fP to \fBraid1\fP. ++Converting from one raid level to another may require multiple steps, ++converting first to intermediate raid levels. + +-.nf +-# lvs -a -o name,segtype,size vg +- LV Type LSize +- lv linear 300.00g ++.B linear to raid6 + +-# lvconvert --type raid1 --mirrors 1 vg/lv ++To convert an LV from linear to raid6: ++.br ++1. convert to raid1 with two images ++.br ++2. convert to raid5 (internally raid5_ls) with two images ++.br ++3. convert to raid5 with three or more stripes (reshape) ++.br ++4. convert to raid6 (internally raid6_ls_6) ++.br ++5. convert to raid6 (internally raid6_zr, reshape) + +-# lvs -a -o name,segtype,size vg +- LV Type LSize +- lv raid1 300.00g +- [lv_rimage_0] linear 300.00g +- [lv_rimage_1] linear 300.00g +- [lv_rmeta_0] linear 3.00m +- [lv_rmeta_1] linear 3.00m +-.fi ++The commands to perform the steps above are: ++.br ++1. lvconvert --type raid1 --mirrors 1 LV ++.br ++2. lvconvert --type raid5 LV ++.br ++3. lvconvert --stripes 3 LV ++.br ++4. lvconvert --type raid6 LV ++.br ++5. lvconvert --type raid6 LV + +-2. Converting an LV from \fBmirror\fP to \fBraid1\fP. ++The final conversion from raid6_ls_6 to raid6_zr is done to avoid the ++potential write/recovery performance reduction in raid6_ls_6 because of ++the dedicated parity device. raid6_zr rotates data and parity blocks to ++avoid this. ++ ++.B linear to striped ++ ++To convert an LV from linear to striped: ++.br ++1. convert to raid1 with two images ++.br ++2. convert to raid5_n ++.br ++3. convert to raid5_n with five 128k stripes (reshape) ++.br ++4. convert raid5_n to striped ++ ++The commands to perform the steps above are: ++.br ++1. lvconvert --type raid1 --mirrors 1 LV ++.br ++2. lvconvert --type raid5_n LV ++.br ++3. lvconvert --stripes 5 --stripesize 128k LV ++.br ++4. lvconvert --type striped LV ++ ++The raid5_n type in step 2 is used because it has dedicated parity SubLVs ++at the end, and can be converted to striped directly. The stripe size is ++increased in step 3 to add extra space for the conversion process. This ++step grows the LV size by a factor of five. After conversion, this extra ++space can be reduced (or used to grow the file system using the LV). ++ ++Reversing these steps will convert a striped LV to linear. ++ ++.B raid6 to striped ++ ++To convert an LV from raid6_nr to striped: ++.br ++1. convert to raid6_n_6 ++.br ++2. convert to striped ++ ++The commands to perform the steps above are: ++.br ++1. lvconvert --type raid6_n_6 LV ++.br ++2. lvconvert --type striped LV + +-.nf +-# lvs -a -o name,segtype,size vg +- LV Type LSize +- lv mirror 100.00g +- [lv_mimage_0] linear 100.00g +- [lv_mimage_1] linear 100.00g +- [lv_mlog] linear 3.00m + + .SS Examples + +-1. Converting an LV from \fBlinear\fP to \fBraid1\fP. ++Converting an LV from \fBlinear\fP to \fBraid1\fP. + + .nf + # lvs -a -o name,segtype,size vg +@@ -962,7 +1013,7 @@ between striped and raid4. + [lv_rmeta_1] linear 3.00m + .fi + +-2. Converting an LV from \fBmirror\fP to \fBraid1\fP. ++Converting an LV from \fBmirror\fP to \fBraid1\fP. + + .nf + # lvs -a -o name,segtype,size vg +@@ -983,28 +1034,17 @@ between striped and raid4. + [lv_rmeta_1] linear 3.00m + .fi + +-3. Converting an LV from \fBlinear\fP to \fBraid1\fP (with 3 images). ++Converting an LV from \fBlinear\fP to \fBraid1\fP (with 3 images). + + .nf +-Start with a linear LV: +- +-# lvcreate -L1G -n lv vg +- +-Convert the linear LV to raid1 with three images +-(original linear image plus 2 mirror images): +- + # lvconvert --type raid1 --mirrors 2 vg/lv + .fi + +-4. Converting an LV from \fBstriped\fP (with 4 stripes) to \fBraid6_n_6\fP. ++Converting an LV from \fBstriped\fP (with 4 stripes) to \fBraid6_n_6\fP. + + .nf +-Start with a striped LV: +- + # lvcreate --stripes 4 -L64M -n lv vg + +-Convert the striped LV to raid6_n_6: +- + # lvconvert --type raid6 vg/lv + + # lvs -a -o lv_name,segtype,sync_percent,data_copies +@@ -1051,7 +1091,9 @@ that is done, the new stripe is unquiesced and used.) + + .SS Examples + +-1. Converting raid6_n_6 to raid6_nr with rotating data/parity. ++(Command output shown in examples may change.) ++ ++Converting raid6_n_6 to raid6_nr with rotating data/parity. + + This conversion naturally follows a previous conversion from striped/raid0 + to raid6_n_6 (shown above). It completes the transition to a more +@@ -1318,7 +1360,8 @@ In case the RaidLV should be converted to striped: + .nf + # lvconvert --type striped vg/lv + Unable to convert LV vg/lv from raid6_nr to striped. +- Converting vg/lv from raid6_nr is directly possible to the following layouts: ++ Converting vg/lv from raid6_nr is directly possible to the \\ ++ following layouts: + raid6_nc + raid6_zr + raid6_la_6 +@@ -1621,7 +1664,9 @@ RAID6 last parity devices + .br + \[bu] + Fixed dedicated last devices (P-Syndrome N-1 and Q-Syndrome N) ++.RS 2 + with striped data used for striped/raid0 conversions ++.RE + .br + \[bu] + Used for RAID Takeover +@@ -1632,7 +1677,10 @@ raid6_{ls,rs,la,ra}_6 + RAID6 last parity device + .br + \[bu] +-Dedicated last parity device used for conversions from/to raid5_{ls,rs,la,ra} ++Dedicated last parity device used for conversions from/to ++.RS 2 ++raid5_{ls,rs,la,ra} ++.RE + + raid6_ls_6 + .br +diff --git a/man/lvmreport.7_main b/man/lvmreport.7_main +index 7a26401..7167df0 100644 +--- a/man/lvmreport.7_main ++++ b/man/lvmreport.7_main +@@ -948,7 +948,7 @@ configuration directly on command line. + + You can obtain the same information with single command where all the + information about PVs, PV segments, LVs and LV segments are obtained +-per VG under a single VG lock for consistency, see also \fBlvm fullreport\fP(8) ++per VG under a single VG lock for consistency, see also \fBlvm-fullreport\fP(8) + man page for more information. The fullreport has its own configuration + settings to define field sets to use, similar to individual reports as + displayed above, but configuration settings have "_full" suffix now. +diff --git a/man/lvmsystemid.7_main b/man/lvmsystemid.7_main +index 8c57042..9b36dc3 100644 +--- a/man/lvmsystemid.7_main ++++ b/man/lvmsystemid.7_main +@@ -94,19 +94,18 @@ corrupting the PVs. See the + section for more information. + + .IP \[bu] 2 +-The system ID does not protect devices in VG from programs other than LVM. ++The system ID does not protect devices in a VG from programs other than LVM. + + .IP \[bu] 2 +-A host using an old version of LVM without the system ID feature will not +-recognize a system ID in VGs from other hosts. Even though the old +-version of LVM is not blocked from reading a VG with a system ID, it is +-blocked from writing to the VG (or its LVs). The system ID feature +-changes the write mode of a VG, making it appear read-only to previous +-versions of LVM. +- +-This also means that if a host downgrades its version of LVM, it would ++A host using an old LVM version (without the system ID feature) will not ++recognize a system ID set in VGs. The old LVM can read a VG with a ++system ID, but is prevented from writing to the VG (or its LVs). ++The system ID feature changes the write mode of a VG, making it appear ++read-only to previous versions of LVM. ++ ++This also means that if a host downgrades to the old LVM version, it would + lose access to any VGs it had created with a system ID. To avoid this, +-the system ID should be removed from VGs before downgrading to an LVM ++the system ID should be removed from local VGs before downgrading LVM to a + version without the system ID feature. + + +diff --git a/man/pvchange.8_des b/man/pvchange.8_des +index 802850f..e914e13 100644 +--- a/man/pvchange.8_des ++++ b/man/pvchange.8_des +@@ -1 +1,4 @@ + pvchange changes PV attributes in the VG. ++ ++For options listed in parentheses, any one is required, after which the ++others are optional. +diff --git a/man/see_also.end b/man/see_also.end +index 9e7d3b2..5b07719 100644 +--- a/man/see_also.end ++++ b/man/see_also.end +@@ -46,6 +46,8 @@ + .BR lvs (8) + .BR lvscan (8) + ++.BR lvm-fullreport (8) ++.BR lvm-lvpoll (8) + .BR lvm2-activation-generator (8) + .BR blkdeactivate (8) + .BR lvmdump (8) +diff --git a/man/vgexport.8_des b/man/vgexport.8_des +index f9fa49c..66d3af3 100644 +--- a/man/vgexport.8_des ++++ b/man/vgexport.8_des +@@ -1,6 +1,6 @@ + vgexport makes inactive VGs unknown to the system. In this state, all the + PVs in the VG can be moved to a different system, from which +-\fBvgimport\fP can then be run. ++\fBvgimport\fP(8) can then be run. + + Most LVM tools ignore exported VGs. + +diff --git a/scripts/fsadm.sh b/scripts/fsadm.sh +index 26be102..ea14efe 100755 +--- a/scripts/fsadm.sh ++++ b/scripts/fsadm.sh +@@ -75,8 +75,9 @@ BLOCKCOUNT= + MOUNTPOINT= + MOUNTED= + REMOUNT= +-PROCMOUNTS="/proc/mounts" +-PROCSELFMOUNTINFO="/proc/self/mountinfo" ++PROCDIR="/proc" ++PROCMOUNTS="$PROCDIR/mounts" ++PROCSELFMOUNTINFO="$PROCDIR/self/mountinfo" + NULL="$DM_DEV_DIR/null" + + IFS_OLD=$IFS +@@ -113,8 +114,11 @@ verbose() { + test -n "$VERB" && echo "$TOOL: $@" || true + } + ++# Support multi-line error messages + error() { +- echo "$TOOL: $@" >&2 ++ for i in "$@" ; do ++ echo "$TOOL: $i" >&2 ++ done + cleanup 1 + } + +@@ -178,52 +182,135 @@ decode_size() { + fi + } + ++decode_major_minor() { ++ # 0x00000fff00 mask MAJOR ++ # 0xfffff000ff mask MINOR ++ ++ #MINOR=$(( $1 / 1048576 )) ++ #MAJOR=$(( ($1 - ${MINOR} * 1048576) / 256 )) ++ #MINOR=$(( $1 - ${MINOR} * 1048576 - ${MAJOR} * 256 + ${MINOR} * 256)) ++ ++ echo "$(( ( $1 >> 8 ) & 4095 )):$(( ( ( $1 >> 12 ) & 268435200 ) | ( $1 & 255 ) ))" ++} ++ + # detect filesystem on the given device + # dereference device name if it is symbolic link + detect_fs() { + VOLUME_ORIG=$1 + VOLUME=${1/#"${DM_DEV_DIR}/"/} +- VOLUME=$("$READLINK" $READLINK_E "$DM_DEV_DIR/$VOLUME") || error "Cannot get readlink \"$1\"" ++ VOLUME=$("$READLINK" $READLINK_E "$DM_DEV_DIR/$VOLUME") ++ test -n "$VOLUME" || error "Cannot get readlink \"$1\"." + RVOLUME=$VOLUME + case "$RVOLUME" in +- # hardcoded /dev since udev does not create these entries elsewhere ++ # hardcoded /dev since udev does not create these entries elsewhere + /dev/dm-[0-9]*) + read &1 && VOLUME="$DM_DEV_DIR/mapper/$SYSVOLUME" +- read &1 || error "Cannot get major:minor for \"$VOLUME\"" ++ read &1 || error "Cannot get major:minor for \"$VOLUME\"." ++ MAJOR=${MAJORMINOR%%:*} ++ MINOR=${MAJORMINOR##*:} + ;; + *) +- STAT=$(stat --format "MAJOR=%t MINOR=%T" ${RVOLUME}) || error "Cannot get major:minor for \"$VOLUME\"" +- eval $STAT +- MAJOR=$((0x${MAJOR})) +- MINOR=$((0x${MINOR})) +- MAJORMINOR=${MAJOR}:${MINOR} ++ STAT=$(stat --format "MAJOR=\$((0x%t)) MINOR=\$((0x%T))" ${RVOLUME}) ++ test -n "$STAT" || error "Cannot get major:minor for \"$VOLUME\"." ++ eval "$STAT" ++ MAJORMINOR="${MAJOR}:${MINOR}" + ;; + esac + # use null device as cache file to be sure about the result + # not using option '-o value' to be compatible with older version of blkid +- FSTYPE=$("$BLKID" -c "$NULL" -s TYPE "$VOLUME") || error "Cannot get FSTYPE of \"$VOLUME\"" ++ FSTYPE=$("$BLKID" -c "$NULL" -s TYPE "$VOLUME") ++ test -n "$FSTYPE" || error "Cannot get FSTYPE of \"$VOLUME\"." + FSTYPE=${FSTYPE##*TYPE=\"} # cut quotation marks + FSTYPE=${FSTYPE%%\"*} +- verbose "\"$FSTYPE\" filesystem found on \"$VOLUME\"" ++ verbose "\"$FSTYPE\" filesystem found on \"$VOLUME\"." ++} ++ ++ ++# Check that passed mounted MAJOR:MINOR is not matching $MAJOR:MINOR of resized $VOLUME ++validate_mounted_major_minor() { ++ test "$1" = "$MAJORMINOR" || { ++ local REFNAME=$(dmsetup info -c -j "${1%%:*}" -m "${1##*:}" -o name --noheadings 2>/dev/null) ++ local CURNAME=$(dmsetup info -c -j "$MAJOR" -m "$MINOR" -o name --noheadings 2>/dev/null) ++ error "Cannot ${CHECK+CHECK}${RESIZE+RESIZE} device \"$VOLUME\" without umounting filesystem $MOUNTED first." \ ++ "Mounted filesystem is using device $CURNAME, but referenced device is $REFNAME." \ ++ "Filesystem utilities currently do not support renamed devices." ++ } ++} ++ ++# ATM fsresize & fsck tools are not able to work properly ++# when mounted device has changed its name. ++# So whenever such device no longer exists with original name ++# abort further command processing ++check_valid_mounted_device() { ++ local MOUNTEDMAJORMINOR ++ local VOL=$("$READLINK" $READLINK_E "$1") ++ local CURNAME=$(dmsetup info -c -j "$MAJOR" -m "$MINOR" -o name --noheadings) ++ local SUGGEST="Possibly device \"$1\" has been renamed to \"$CURNAME\"?" ++ ++ # more confused, device is not DM.... ++ test -n "$CURNAME" || SUGGEST="Mounted volume is not a device mapper device???" ++ ++ test -n "$VOL" || ++ error "Cannot access device \"$1\" referenced by mounted filesystem \"$MOUNTED\"." \ ++ "$SUGGEST" \ ++ "Filesystem utilities currently do not support renamed devices." ++ ++ case "$VOL" in ++ # hardcoded /dev since udev does not create these entries elsewhere ++ /dev/dm-[0-9]*) ++ read &1 || error "Cannot get major:minor for \"$VOLUME\"." ++ ;; ++ *) ++ STAT=$(stat --format "MOUNTEDMAJORMINOR=\$((0x%t)):\$((0x%T))" "$VOL") ++ test -n "$STAT" || error "Cannot get major:minor for \"$VOLUME\"." ++ eval "$STAT" ++ ;; ++ esac ++ ++ validate_mounted_major_minor "$MOUNTEDMAJORMINOR" + } + +-detect_mounted_with_proc_self_mountinfo() { +- MOUNTED=$("$GREP" "^[0-9]* [0-9]* $MAJORMINOR " "$PROCSELFMOUNTINFO") ++detect_mounted_with_proc_self_mountinfo() { ++ # Check self mountinfo ++ # grab major:minor mounted_device mount_point ++ MOUNTED=$("$GREP" "^[0-9]* [0-9]* $MAJORMINOR " "$PROCSELFMOUNTINFO" 2>/dev/null | head -1) ++ ++ # If device is opened and not yet found as self mounted ++ # check all other mountinfos (since it can be mounted in cgroups) ++ # Use 'find' to not fail on to long list of args with too many pids ++ # only 1st. line is needed ++ test -z "$MOUNTED" && ++ test $(dmsetup info -c --noheading -o open -j "$MAJOR" -m "$MINOR") -gt 0 && ++ MOUNTED=$(find "$PROCDIR" -maxdepth 2 -name mountinfo -print0 | xargs -0 "$GREP" "^[0-9]* [0-9]* $MAJORMINOR " 2>/dev/null | head -1 2>/dev/null) + +- # extract 5th field which is mount point ++ # TODO: for performance compare with sed and stop with 1st. match: ++ # sed -n "/$MAJORMINOR/ {;p;q;}" ++ ++ # extract 2nd field after ' - ' separator as mouted device ++ MOUNTDEV=$(echo ${MOUNTED##* - } | cut -d ' ' -f 2) ++ MOUNTDEV=$(echo -n -e ${MOUNTDEV}) ++ ++ # extract 5th field as mount point + # echo -e translates \040 to spaces +- MOUNTED=$(echo ${MOUNTED} | cut -d " " -f 5) ++ MOUNTED=$(echo ${MOUNTED} | cut -d ' ' -f 5) + MOUNTED=$(echo -n -e ${MOUNTED}) + +- test -n "$MOUNTED" ++ test -n "$MOUNTED" || return 1 # Not seen mounted anywhere ++ ++ check_valid_mounted_device "$MOUNTDEV" + } + +-detect_mounted_with_proc_mounts() { ++# With older systems without /proc/*/mountinfo we may need to check ++# every mount point as cannot easily depend on the name of mounted ++# device (which could have been renamed). ++# We need to visit every mount point and check it's major minor ++detect_mounted_with_proc_mounts() { + MOUNTED=$("$GREP" "^$VOLUME[ \t]" "$PROCMOUNTS") + + # for empty string try again with real volume name + test -z "$MOUNTED" && MOUNTED=$("$GREP" "^$RVOLUME[ \t]" "$PROCMOUNTS") + ++ MOUNTDEV=$(echo -n -e ${MOUNTED%% *}) + # cut device name prefix and trim everything past mountpoint + # echo translates \040 to spaces + MOUNTED=${MOUNTED#* } +@@ -231,24 +318,43 @@ detect_mounted_with_proc_mounts() { + + # for systems with different device names - check also mount output + if test -z "$MOUNTED" ; then ++ # will not work with spaces in paths + MOUNTED=$(LC_ALL=C "$MOUNT" | "$GREP" "^$VOLUME[ \t]") + test -z "$MOUNTED" && MOUNTED=$(LC_ALL=C "$MOUNT" | "$GREP" "^$RVOLUME[ \t]") ++ MOUNTDEV=${MOUNTED%% on *} + MOUNTED=${MOUNTED##* on } + MOUNTED=${MOUNTED% type *} # allow type in the mount name + fi + +- test -n "$MOUNTED" ++ if test -n "$MOUNTED" ; then ++ check_valid_mounted_device "$MOUNTDEV" ++ return 0 # mounted ++ fi ++ ++ # If still nothing found and volume is in use ++ # check every known mount point against MAJOR:MINOR ++ if test $(dmsetup info -c --noheading -o open -j "$MAJOR" -m "$MINOR") -gt 0 ; then ++ while IFS=$'\n' read -r i ; do ++ MOUNTDEV=$(echo -n -e ${i%% *}) ++ MOUNTED=${i#* } ++ MOUNTED=$(echo -n -e ${MOUNTED%% *}) ++ STAT=$(stat --format "%d" $MOUNTED) ++ validate_mounted_major_minor $(decode_major_minor "$STAT") ++ done < "$PROCMOUNTS" ++ fi ++ ++ return 1 # nothing is mounted + } + + # check if the given device is already mounted and where + # FIXME: resolve swap usage and device stacking +-detect_mounted() { ++detect_mounted() { + if test -e "$PROCSELFMOUNTINFO"; then + detect_mounted_with_proc_self_mountinfo + elif test -e "$PROCMOUNTS"; then + detect_mounted_with_proc_mounts + else +- error "Cannot detect mounted device \"$VOLUME\"" ++ error "Cannot detect mounted device \"$VOLUME\"." + fi + } + +@@ -257,10 +363,13 @@ detect_device_size() { + # check if blockdev supports getsize64 + "$BLOCKDEV" 2>&1 | "$GREP" getsize64 >"$NULL" + if test $? -eq 0; then +- DEVSIZE=$("$BLOCKDEV" --getsize64 "$VOLUME") || error "Cannot read size of device \"$VOLUME\"" ++ DEVSIZE=$("$BLOCKDEV" --getsize64 "$VOLUME") ++ test -n "$DEVSIZE" || error "Cannot read size of device \"$VOLUME\"." + else +- DEVSIZE=$("$BLOCKDEV" --getsize "$VOLUME") || error "Cannot read size of device \"$VOLUME\"" +- SSSIZE=$("$BLOCKDEV" --getss "$VOLUME") || error "Cannot block size read device \"$VOLUME\"" ++ DEVSIZE=$("$BLOCKDEV" --getsize "$VOLUME") ++ test -n "$DEVSIZE" || error "Cannot read size of device \"$VOLUME\"." ++ SSSIZE=$("$BLOCKDEV" --getss "$VOLUME") ++ test -n "$SSSIZE" || error "Cannot read sector size of device \"$VOLUME\"." + DEVSIZE=$(($DEVSIZE * $SSSIZE)) + fi + } +@@ -273,14 +382,14 @@ round_up_block_size() { + } + + temp_mount() { +- dry "$MKDIR" -p -m 0000 "$TEMPDIR" || error "Failed to create $TEMPDIR" +- dry "$MOUNT" "$VOLUME" "$TEMPDIR" || error "Failed to mount $TEMPDIR" ++ dry "$MKDIR" -p -m 0000 "$TEMPDIR" || error "Failed to create $TEMPDIR." ++ dry "$MOUNT" "$VOLUME" "$TEMPDIR" || error "Failed to mount $TEMPDIR." + } + + temp_umount() { +- dry "$UMOUNT" "$TEMPDIR" || error "Failed to umount \"$TEMPDIR\"" +- dry "$RMDIR" "${TEMPDIR}" || error "Failed to remove \"$TEMPDIR\"" +- dry "$RMDIR" "${TEMPDIR%%m}" || error "Failed to remove \"${TEMPDIR%%m}\"" ++ dry "$UMOUNT" "$TEMPDIR" || error "Failed to umount \"$TEMPDIR\"." ++ dry "$RMDIR" "${TEMPDIR}" || error "Failed to remove \"$TEMPDIR\"," ++ dry "$RMDIR" "${TEMPDIR%%m}" || error "Failed to remove \"${TEMPDIR%%m}\"." + } + + yes_no() { +@@ -292,19 +401,24 @@ yes_no() { + + while read -r -s -n 1 ANS ; do + case "$ANS" in +- "y" | "Y" | "") echo y ; return 0 ;; +- "n" | "N") echo n ; return 1 ;; ++ "y" | "Y" ) echo y ; return 0 ;; ++ "" ) if [ -t 1 ] ; then ++ echo y ; return 0 ++ fi ;; + esac + done ++ ++ echo n ++ return 1 + } + + try_umount() { + yes_no "Do you want to unmount \"$MOUNTED\"" && dry "$UMOUNT" "$MOUNTED" && return 0 +- error "Cannot proceed with mounted filesystem \"$MOUNTED\"" ++ error "Cannot proceed with mounted filesystem \"$MOUNTED\"." + } + + validate_parsing() { +- test -n "$BLOCKSIZE" && test -n "$BLOCKCOUNT" || error "Cannot parse $1 output" ++ test -n "$BLOCKSIZE" && test -n "$BLOCKCOUNT" || error "Cannot parse $1 output." + } + #################################### + # Resize ext2/ext3/ext4 filesystem +@@ -312,6 +426,9 @@ validate_parsing() { + # - unmounted for downsize + #################################### + resize_ext() { ++ local IS_MOUNTED=0 ++ detect_mounted && IS_MOUNTED=1 ++ + verbose "Parsing $TUNE_EXT -l \"$VOLUME\"" + for i in $(LC_ALL=C "$TUNE_EXT" -l "$VOLUME"); do + case "$i" in +@@ -324,7 +441,7 @@ resize_ext() { + FSFORCE=$FORCE + + if [ "$NEWBLOCKCOUNT" -lt "$BLOCKCOUNT" -o "$EXTOFF" -eq 1 ]; then +- detect_mounted && verbose "$RESIZE_EXT needs unmounted filesystem" && try_umount ++ test $IS_MOUNTED -eq 1 && verbose "$RESIZE_EXT needs unmounted filesystem" && try_umount + REMOUNT=$MOUNTED + if test -n "$MOUNTED" ; then + # Forced fsck -f for umounted extX filesystem. +@@ -374,7 +491,7 @@ resize_xfs() { + MOUNTPOINT=$MOUNTED + if [ -z "$MOUNTED" ]; then + MOUNTPOINT=$TEMPDIR +- temp_mount || error "Cannot mount Xfs filesystem" ++ temp_mount || error "Cannot mount Xfs filesystem." + fi + verbose "Parsing $TUNE_XFS \"$MOUNTPOINT\"" + for i in $(LC_ALL=C "$TUNE_XFS" "$MOUNTPOINT"); do +@@ -392,7 +509,7 @@ resize_xfs() { + elif [ $NEWBLOCKCOUNT -eq $BLOCKCOUNT ]; then + verbose "Xfs filesystem already has the right size" + else +- error "Xfs filesystem shrinking is unsupported" ++ error "Xfs filesystem shrinking is unsupported." + fi + } + +@@ -412,8 +529,8 @@ resize() { + "ext3"|"ext2"|"ext4") resize_ext $NEWSIZE ;; + "reiserfs") resize_reiser $NEWSIZE ;; + "xfs") resize_xfs $NEWSIZE ;; +- *) error "Filesystem \"$FSTYPE\" on device \"$VOLUME\" is not supported by this tool" ;; +- esac || error "Resize $FSTYPE failed" ++ *) error "Filesystem \"$FSTYPE\" on device \"$VOLUME\" is not supported by this tool." ;; ++ esac || error "Resize $FSTYPE failed." + cleanup 0 + } + +@@ -494,12 +611,12 @@ for i in "$TUNE_EXT" "$RESIZE_EXT" "$TUNE_REISER" "$RESIZE_REISER" \ + test -n "$i" || error "Required command definitions in the script are missing!" + done + +-"$LVM" version >"$NULL" 2>&1 || error "Could not run lvm binary \"$LVM\"" ++"$LVM" version >"$NULL" 2>&1 || error "Could not run lvm binary \"$LVM\"." + $("$READLINK" -e / >"$NULL" 2>&1) || READLINK_E="-f" + TEST64BIT=$(( 1000 * 1000000000000 )) +-test "$TEST64BIT" -eq 1000000000000000 || error "Shell does not handle 64bit arithmetic" +-$(echo Y | "$GREP" Y >"$NULL") || error "Grep does not work properly" +-test $("$DATE" -u -d"Jan 01 00:00:01 1970" +%s) -eq 1 || error "Date translation does not work" ++test "$TEST64BIT" -eq 1000000000000000 || error "Shell does not handle 64bit arithmetic." ++$(echo Y | "$GREP" Y >"$NULL") || error "Grep does not work properly." ++test $("$DATE" -u -d"Jan 01 00:00:01 1970" +%s) -eq 1 || error "Date translation does not work." + + + if [ "$#" -eq 0 ] ; then +diff --git a/test/dbus/lvmdbustest.py b/test/dbus/lvmdbustest.py +index 50f6ad4..3dca9d5 100755 +--- a/test/dbus/lvmdbustest.py ++++ b/test/dbus/lvmdbustest.py +@@ -18,6 +18,8 @@ import pyudev + from testlib import * + import testlib + from subprocess import Popen, PIPE ++from glob import glob ++import os + + g_tmo = 0 + +@@ -68,8 +70,12 @@ def lv_n(suffix=None): + return g_prefix + rs(8, s) + + ++def _is_testsuite_pv(pv_name): ++ return g_prefix != "" and pv_name[-1].isdigit() and pv_name[:-1].endswith(g_prefix + "pv") ++ ++ + def is_nested_pv(pv_name): +- return pv_name.count('/') == 3 ++ return pv_name.count('/') == 3 and not _is_testsuite_pv(pv_name) + + + def _root_pv_name(res, pv_name): +@@ -239,6 +245,7 @@ class TestDbusService(unittest.TestCase): + + # Check to make sure the PVs we had to start exist, else re-create + # them ++ self.objs, self.bus = get_objects() + if len(self.pvs) != len(self.objs[PV_INT]): + for p in self.pvs: + found = False +@@ -714,9 +721,9 @@ class TestDbusService(unittest.TestCase): + LV_BASE_INT) + self._validate_lookup("%s/%s" % (vg.Name, lv_name), lv.object_path) + +- def _create_lv(self, thinpool=False, size=None, vg=None): ++ def _create_lv(self, thinpool=False, size=None, vg=None, suffix=None): + +- lv_name = lv_n() ++ lv_name = lv_n(suffix=suffix) + interfaces = list(LV_BASE_INT) + + if thinpool: +@@ -1840,7 +1847,8 @@ class TestDbusService(unittest.TestCase): + self.assertIn(pv_object_path, vg.Vg.Pvs, + "Expecting PV object path in Vg.Pvs") + +- lv = self._create_lv(vg=vg.Vg, size=vg.Vg.FreeBytes) ++ lv = self._create_lv(vg=vg.Vg, size=vg.Vg.FreeBytes, ++ suffix="_pv") + device_path = '/dev/%s/%s' % (vg.Vg.Name, lv.LvCommon.Name) + new_pv_object_path = self._pv_create(device_path) + +@@ -1865,6 +1873,46 @@ class TestDbusService(unittest.TestCase): + for i in range(0, 5): + pv_object_path = self._create_nested(pv_object_path) + ++ def test_pv_symlinks(self): ++ # Lets take one of our test PVs, pvremove it, find a symlink to it ++ # and re-create using the symlink to ensure we return an object ++ # path to it. Additionally, we will take the symlink and do a lookup ++ # (Manager.LookUpByLvmId) using it and the original device path to ++ # ensure that we can find the PV. ++ symlink = None ++ ++ pv = self.objs[PV_INT][0] ++ pv_device_path = pv.Pv.Name ++ ++ self._pv_remove(pv) ++ ++ # Make sure we no longer find the pv ++ rc = self._lookup(pv_device_path) ++ self.assertEqual(rc, '/') ++ ++ # Lets locate a symlink for it ++ devices = glob('/dev/disk/*/*') ++ for d in devices: ++ if pv_device_path == os.path.realpath(d): ++ symlink = d ++ break ++ ++ self.assertIsNotNone(symlink, "We expected to find at least 1 symlink!") ++ ++ # Make sure symlink look up fails too ++ rc = self._lookup(symlink) ++ self.assertEqual(rc, '/') ++ ++ pv_object_path = self._pv_create(symlink) ++ self.assertNotEqual(pv_object_path, '/') ++ ++ pv_proxy = ClientProxy(self.bus, pv_object_path, interfaces=(PV_INT, )) ++ self.assertEqual(pv_proxy.Pv.Name, pv_device_path) ++ ++ # Lets check symlink lookup ++ self.assertEqual(pv_object_path, self._lookup(symlink)) ++ self.assertEqual(pv_object_path, self._lookup(pv_device_path)) ++ + + class AggregateResults(object): + +diff --git a/test/shell/fsadm-renamed.sh b/test/shell/fsadm-renamed.sh +new file mode 100644 +index 0000000..9f3cd3c +--- /dev/null ++++ b/test/shell/fsadm-renamed.sh +@@ -0,0 +1,116 @@ ++#!/bin/sh ++# Copyright (C) 2017 Red Hat, Inc. All rights reserved. ++# ++# This copyrighted material is made available to anyone wishing to use, ++# modify, copy, or redistribute it subject to the terms and conditions ++# of the GNU General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++test_description='Exercise fsadm operation on renamed device' ++SKIP_WITH_LVMLOCKD=1 ++SKIP_WITH_LVMPOLLD=1 ++ ++. lib/inittest ++ ++aux prepare_vg 1 80 ++ ++vg_lv=$vg/$lv1 ++vg_lv_ren=${vg_lv}_renamed ++ ++dev_vg_lv="$DM_DEV_DIR/$vg_lv" ++dev_vg_lv_ren="$DM_DEV_DIR/$vg_lv_ren" ++ ++mount_dir="mnt" ++mount_space_dir="mnt space dir" ++mount_dolar_dir="mnt \$SPACE dir" ++# for recursive call ++export LVM_BINARY=$(which lvm) ++ ++test ! -d "$mount_dir" && mkdir "$mount_dir" ++test ! -d "$mount_space_dir" && mkdir "$mount_space_dir" ++test ! -d "$mount_dolar_dir" && mkdir "$mount_dolar_dir" ++ ++cleanup_mounted_and_teardown() ++{ ++ umount "$mount_dir" || true ++ umount "$mount_space_dir" || true ++ umount "$mount_dolar_dir" || true ++ aux teardown ++} ++ ++# Test for block sizes != 1024 (rhbz #480022) ++trap 'cleanup_mounted_and_teardown' EXIT ++ ++# Iterate over supported filesystems ++for i in mkfs.ext3 mkfs.xfs mkfs.reiserfs ++do ++ ++if not which "$i" ; then ++ echo "Skipping tests for missing $i" ++ continue ++fi ++ ++lvcreate -n $lv1 -L20M $vg ++ ++case "$i" in ++*ext3) MKFS_ARGS="-b1024 -j" ;; ++*xfs) MKFS_ARGS="-l internal,size=1000b -f" ;; ++*reiserfs) MKFS_ARGS="-s 513 -f" ;; ++esac ++ ++echo "$i" ++"$i" $MKFS_ARGS "$dev_vg_lv" ++ ++# Adding couple udev wait ops as some older systemd ++# might get confused and was 'randomly/racy' umounting ++# devices just mounted. ++# ++# See for explanation: ++# https://github.com/systemd/systemd/commit/628c89cc68ab96fce2de7ebba5933725d147aecc ++# https://github.com/systemd/systemd/pull/2017 ++aux udev_wait ++ ++mount "$dev_vg_lv" "$mount_dir" ++ ++aux udev_wait ++ ++lvrename $vg_lv $vg_lv_ren ++ ++mount | tee out ++grep $vg out ++ ++# fails on renamed LV ++fail lvresize -L+10M -r $vg_lv_ren ++ ++# fails on unknown mountpoint (FIXME: umount) ++not umount "$dev_vg_lv" ++ ++lvcreate -L20 -n $lv1 $vg ++"$i" $MKFS_ARGS "$dev_vg_lv" ++ ++aux udev_wait ++ ++mount "$dev_vg_lv" "$mount_dolar_dir" ++ ++mount | tee out ++grep $vg out ++ ++not lvresize -L+10M -r $vg_lv_ren ++ ++umount "$mount_dir" ++ ++# FIXME: lvresize CANNOT handle/propagage '--yes' to fsadm ++echo y | lvresize -L+10M -r $vg_lv ++ ++aux udev_wait ++ ++umount "$mount_dolar_dir" ++ ++lvremove -ff $vg ++ ++done ++ ++vgremove -ff $vg +diff --git a/test/shell/lvchange-raid.sh b/test/shell/lvchange-raid.sh +index 34a40dc..7418cd5 100644 +--- a/test/shell/lvchange-raid.sh ++++ b/test/shell/lvchange-raid.sh +@@ -42,6 +42,7 @@ run_writemostly_check() { + + printf "#\n#\n#\n# %s/%s (%s): run_writemostly_check\n#\n#\n#\n" \ + $vg $lv $segtype ++ aux wait_for_sync $vg $lv + + # No writemostly flag should be there yet. + check lv_attr_bit health $vg/${lv}_rimage_0 "-" +diff --git a/test/shell/lvconvert-raid-regionsize.sh b/test/shell/lvconvert-raid-regionsize.sh +index 9884c1b..d2c8a54 100644 +--- a/test/shell/lvconvert-raid-regionsize.sh ++++ b/test/shell/lvconvert-raid-regionsize.sh +@@ -64,6 +64,7 @@ _test_regionsizes raid1 + # Clean up + lvremove --yes $vg + ++if aux have_raid 1 10 1; then + # Create 5-way raid6 + lvcreate --yes -aey --type raid6 -i 3 --stripesize 128K -R 256K -L8M -n $lv1 $vg + check lv_field $vg/$lv1 segtype "raid6" +@@ -78,7 +79,11 @@ _test_regionsizes raid6 + + # Clean up + lvremove --yes $vg ++else ++ echo "Skipping RAID6 tests" ++fi + ++if aux have_raid 1 10 1; then + # Create 6-way raid01 + lvcreate --yes -aey --type raid10 -i 3 -m 1 --stripesize 128K -R 256K -L8M -n $lv1 $vg + check lv_field $vg/$lv1 segtype "raid10" +@@ -90,5 +95,8 @@ aux wait_for_sync $vg $lv1 + fsck -fn "$DM_DEV_DIR/$vg/$lv1" + + _test_regionsizes raid10 ++else ++ echo "Skipping RAID10 tests" ++fi + + vgremove -ff $vg +diff --git a/test/shell/lvconvert-raid-reshape-striped_to_linear.sh b/test/shell/lvconvert-raid-reshape-striped_to_linear.sh +index 4051862..d926827 100644 +--- a/test/shell/lvconvert-raid-reshape-striped_to_linear.sh ++++ b/test/shell/lvconvert-raid-reshape-striped_to_linear.sh +@@ -79,7 +79,7 @@ aux wait_for_sync $vg $lv1 1 + fsck -fn $DM_DEV_DIR/$vg/$lv1 + + # Remove the now freed legs +-lvconvert --stripes 1 $vg/$lv1 ++lvconvert -y --stripes 1 $vg/$lv1 + check lv_first_seg_field $vg/$lv1 segtype "raid5_n" + check lv_first_seg_field $vg/$lv1 data_stripes 1 + check lv_first_seg_field $vg/$lv1 stripes 2 +diff --git a/test/shell/lvconvert-raid.sh b/test/shell/lvconvert-raid.sh +index e173d66..fba7864 100644 +--- a/test/shell/lvconvert-raid.sh ++++ b/test/shell/lvconvert-raid.sh +@@ -223,6 +223,67 @@ for i in 1 2 3 ; do + done + + ########################################### ++# Upconverted RAID1 should not allow loss of primary ++# - don't allow removal of primary while syncing ++# - DO allow removal of secondaries while syncing ++########################################### ++aux delay_dev $dev2 0 100 ++lvcreate -aey -l 2 -n $lv1 $vg $dev1 $dev2 ++lvconvert -y -m 1 $vg/$lv1 \ ++ --config 'global { mirror_segtype_default = "raid1" }' ++lvs --noheadings -o attr $vg/$lv1 | grep '^[[:space:]]*r' ++not lvconvert --yes -m 0 $vg/$lv1 $dev1 ++lvconvert --yes -m 0 $vg/$lv1 $dev2 ++aux enable_dev $dev2 ++lvremove -ff $vg ++ ++########################################### ++# lvcreated RAID1 should allow all down-conversion ++# - DO allow removal of primary while syncing ++# - DO allow removal of secondaries while syncing ++########################################### ++aux delay_dev $dev2 0 100 ++lvcreate --type raid1 -m 2 -aey -l 2 -n $lv1 $vg $dev1 $dev2 $dev3 ++lvconvert --yes -m 1 $vg/$lv1 $dev3 ++lvconvert --yes -m 0 $vg/$lv1 $dev1 ++aux enable_dev $dev2 ++lvremove -ff $vg ++ ++########################################### ++# Converting from 2-way RAID1 to 3-way ++# - DO allow removal of one of primary sources ++# - Do not allow removal of all primary sources ++########################################### ++lvcreate --type raid1 -m 1 -aey -l 2 -n $lv1 $vg $dev1 $dev2 ++aux wait_for_sync $vg $lv1 ++aux delay_dev $dev3 0 100 ++lvconvert --yes -m +1 $vg/$lv1 $dev3 ++# should allow 1st primary to be removed ++lvconvert --yes -m -1 $vg/$lv1 $dev1 ++# should NOT allow last primary to be removed ++not lvconvert --yes -m -1 $vg/$lv1 $dev2 ++# should allow non-primary to be removed ++lvconvert --yes -m 0 $vg/$lv1 $dev3 ++aux enable_dev $dev3 ++lvremove -ff $vg ++ ++########################################### ++# Converting from 2-way RAID1 to 3-way ++# - Should allow removal of two devices, ++# as long as they aren't both primary ++########################################### ++lvcreate --type raid1 -m 1 -aey -l 2 -n $lv1 $vg $dev1 $dev2 ++aux wait_for_sync $vg $lv1 ++aux delay_dev $dev3 0 100 ++lvconvert --yes -m +1 $vg/$lv1 $dev3 ++# should NOT allow both primaries to be removed ++not lvconvert -m 0 $vg/$lv1 $dev1 $dev2 ++# should allow primary + non-primary ++lvconvert --yes -m 0 $vg/$lv1 $dev1 $dev3 ++aux enable_dev $dev3 ++lvremove -ff $vg ++ ++########################################### + # Device Replacement Testing + ########################################### + # RAID1: Replace up to n-1 devices - trying different combinations +diff --git a/test/shell/lvconvert-thin.sh b/test/shell/lvconvert-thin.sh +index c08b95a..992a1f2 100644 +--- a/test/shell/lvconvert-thin.sh ++++ b/test/shell/lvconvert-thin.sh +@@ -135,7 +135,7 @@ lvremove -f $vg + if test "$TSIZE" = 64T; then + lvcreate -L24T -n $lv1 $vg + # Warning about bigger then needed (24T data and 16G -> 128K chunk) +-lvconvert --yes -c 64 --thinpool $vg/$lv1 2>&1 | tee err ++fail lvconvert --yes -c 64 --thinpool $vg/$lv1 2>&1 | tee err + grep "WARNING: Chunk size is too small" err + lvremove -f $vg + fi +diff --git a/test/shell/lvcreate-cache.sh b/test/shell/lvcreate-cache.sh +index faa78b2..410e105 100644 +--- a/test/shell/lvcreate-cache.sh ++++ b/test/shell/lvcreate-cache.sh +@@ -26,6 +26,7 @@ aux prepare_vg 5 80000 + + aux lvmconf 'global/cache_disabled_features = [ "policy_smq" ]' + ++ + ####################### + # Cache_Pool creation # + ####################### +@@ -62,6 +63,11 @@ grep "No command with matching syntax recognised" err + # Check nothing has been created yet + check vg_field $vg lv_count 0 + ++# Checks that argument passed with --cachepool is really a cache-pool ++lvcreate -an -l 1 -n $lv1 $vg ++# Hint: nice way to 'tee' only stderr.log so we can check it's log_error() ++fail lvcreate -L10 --cachepool $vg/$lv1 2> >(tee -a stderr.log >&2) ++grep "not a cache pool" stderr.log + + # With --type cache-pool we are clear which segtype has to be created + lvcreate -l 1 --type cache-pool $vg/pool1 +diff --git a/test/shell/lvcreate-large-raid.sh b/test/shell/lvcreate-large-raid.sh +index ca3f715..7ec140b 100644 +--- a/test/shell/lvcreate-large-raid.sh ++++ b/test/shell/lvcreate-large-raid.sh +@@ -101,7 +101,15 @@ lvremove -ff $vg1 + lvcreate -aey -L 200T -n $lv1 $vg1 + lvconvert -y --type raid1 -m 1 $vg1/$lv1 + check lv_field $vg1/$lv1 size "200.00t" +-check raid_leg_status $vg1 $lv1 "aa" ++if aux have_raid 1 9 0; then ++ # The 1.9.0 version of dm-raid is capable of performing ++ # linear -> RAID1 upconverts as "recover" not "resync" ++ # The LVM code now checks the dm-raid version when ++ # upconverting and if 1.9.0+ is found, it uses "recover" ++ check raid_leg_status $vg1 $lv1 "Aa" ++else ++ check raid_leg_status $vg1 $lv1 "aa" ++fi + lvremove -ff $vg1 + + # bz837927 END +diff --git a/test/shell/lvcreate-raid-nosync.sh b/test/shell/lvcreate-raid-nosync.sh +index 1f01f47..bd7ec81 100644 +--- a/test/shell/lvcreate-raid-nosync.sh ++++ b/test/shell/lvcreate-raid-nosync.sh +@@ -35,7 +35,7 @@ _sync() { + + # Delay 1st leg so that rebuilding status characters + # can be read before resync finished too quick. +-aux delay_dev "$dev1" 0 90 $(get first_extent_sector "$dev1") ++aux delay_dev "$dev1" 0 100 $(get first_extent_sector "$dev1") + + # raid0/raid0_meta don't support resynchronization + for r in raid0 raid0_meta +@@ -58,7 +58,7 @@ lvremove --yes $vg/$lv1 + for r in $segtypes + do + # raid4/5 support resynchronization +- lvcreate --type $r -Zn -i 3 -l 4 -n $lv1 $vg ++ lvcreate --type $r -Zn -i 3 -L10 -n $lv1 $vg + check raid_leg_status $vg $lv1 "aaaa" + _sync "AAAA" + +@@ -77,7 +77,7 @@ _sync "AAAAA" + not lvcreate --type raid6 --nosync -Zn -i 3 -l 1 -n $lv1 $vg + + # raid10 supports resynchronization +-lvcreate --type raid10 -m 1 -Zn -i 3 -l 4 -n $lv1 $vg ++lvcreate --type raid10 -m 1 -Zn -i 3 -L10 -n $lv1 $vg + check raid_leg_status $vg $lv1 "aaaaaa" + _sync "AAAAAA" + +diff --git a/test/shell/lvcreate-thin-big.sh b/test/shell/lvcreate-thin-big.sh +index da7812f..9c84288 100644 +--- a/test/shell/lvcreate-thin-big.sh ++++ b/test/shell/lvcreate-thin-big.sh +@@ -36,7 +36,7 @@ grep "WARNING: Minimum" out + lvcreate -L4M --chunksize 64k --poolmetadatasize 17G -T $vg/pool2 2>out + grep "WARNING: Maximum" out + check lv_field $vg/pool1_tmeta size "2.00m" +-check lv_field $vg/pool2_tmeta size "16.00g" ++check lv_field $vg/pool2_tmeta size "15.81g" + + # Check can start and see thinpool with metadata size above kernel limit + lvcreate -L4M --poolmetadatasize 16G -T $vg/poolM +diff --git a/test/shell/lvresize-full.sh b/test/shell/lvresize-full.sh +index 17ee6aa..3eef11a 100644 +--- a/test/shell/lvresize-full.sh ++++ b/test/shell/lvresize-full.sh +@@ -20,6 +20,7 @@ SKIP_WITH_LVMPOLLD=1 + FSCK=${FSCK-fsck} + MKFS=${MKFS-mkfs.ext3} + RESIZEFS=${RESIZEFS-resize2fs} ++export LVM_BINARY=$(which lvm) + + which $FSCK || skip + which $MKFS || skip +diff --git a/test/shell/thin-dmeventd-warns.sh b/test/shell/thin-dmeventd-warns.sh +new file mode 100644 +index 0000000..d22863b +--- /dev/null ++++ b/test/shell/thin-dmeventd-warns.sh +@@ -0,0 +1,85 @@ ++#!/bin/sh ++ ++# Copyright (C) 2017 Red Hat, Inc. All rights reserved. ++# ++# This copyrighted material is made available to anyone wishing to use, ++# modify, copy, or redistribute it subject to the terms and conditions ++# of the GNU General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ ++# test if dmeventd produces multiple warnings when pools runs above 80% ++ ++SKIP_WITH_LVMLOCKD=1 ++SKIP_WITH_LVMPOLLD=1 ++SKIP_WITH_CLVMD=1 ++SKIP_WITH_LVMETAD=1 ++ ++. lib/inittest ++ ++which blkdiscard || skip ++ ++percent_() { ++ get lv_field $vg/pool data_percent | cut -d. -f1 ++} ++ ++wait_warn_() { ++ ++ for i in $(seq 1 7) ++ do ++ test $(egrep "WARNING: Thin pool.*is now" debug.log_DMEVENTD_out | wc -l) -eq $1 && return 0 ++ sleep 2 ++ done ++ ++ die "Waiting too log for dmeventd log warning" ++} ++# ++# Main ++# ++aux have_thin 1 0 0 || skip ++ ++aux prepare_dmeventd ++aux prepare_vg ++ ++lvcreate -L8 -V8 -T $vg/pool -n $lv1 ++ ++ ++dd if=/dev/zero of="$DM_DEV_DIR/$vg/$lv1" bs=256K count=26 ++test $(percent_) -gt 80 ++ ++# Give it some time to dmeventd to log WARNING ++wait_warn_ 1 ++ ++dd if=/dev/zero of="$DM_DEV_DIR/$vg/$lv1" bs=256K count=30 ++test $(percent_) -gt 90 ++ ++# Give it some time to dmeventd to log WARNING ++wait_warn_ 2 ++ ++dd if=/dev/zero of="$DM_DEV_DIR/$vg/$lv1" bs=1M count=8 ++test $(percent_) -eq 100 ++ ++wait_warn_ 3 ++ ++blkdiscard "$DM_DEV_DIR/$vg/$lv1" ++ ++# FIXME: Enforce thin-pool metadata commit with flushing status ++dmsetup status ${vg}-pool-tpool ++# Wait for thin-pool monitoring to notice lower values ++sleep 11 ++# ATM dmeventd is not logging event for thin-pool getting ++# below 'WARNED' threshold. ++ ++ ++dd if=/dev/zero of="$DM_DEV_DIR/$vg/$lv1" bs=256K count=30 ++test $(percent_) -gt 90 ++ ++lvs -a $vg ++dmsetup status ${vg}-pool-tpool ++ ++# Check pool again Warns ++wait_warn_ 4 ++ ++vgremove -f $vg +diff --git a/test/shell/thin-large.sh b/test/shell/thin-large.sh +new file mode 100644 +index 0000000..da5a7ec +--- /dev/null ++++ b/test/shell/thin-large.sh +@@ -0,0 +1,54 @@ ++#!/bin/sh ++# Copyright (C) 2017 Red Hat, Inc. All rights reserved. ++# ++# This copyrighted material is made available to anyone wishing to use, ++# modify, copy, or redistribute it subject to the terms and conditions ++# of the GNU General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ ++# 'Exercise logic around boundary sizes of thin-pool data and chunksize ++ ++SKIP_WITH_LVMLOCKD=1 ++SKIP_WITH_LVMPOLLD=1 ++ ++. lib/inittest ++ ++# FIXME update test to make something useful on <16T ++aux can_use_16T || skip ++ ++aux have_thin 1 0 0 || skip ++ ++# Prepare ~1P sized devices ++aux prepare_vg 1 1000000000 ++ ++lvcreate -an -T -L250T $vg/pool250 ++ ++lvcreate -an -T -L250T --poolmetadatasize 16G $vg/pool16 ++ ++fail lvcreate -an -T -L250T --chunksize 64K --poolmetadatasize 16G $vg/pool64 ++ ++# Creation of thin-pool with proper chunk-size but not enough metadata size ++# which can grow later needs to pass ++lvcreate -an -T -L250T --chunksize 1M --poolmetadatasize 4G $vg/pool1024 ++ ++# Creation of chunk should fit ++lvcreate -an -T -L12T --chunksize 64K --poolmetadatasize 16G $vg/pool64 ++ ++check lv_field $vg/pool64 chunksize "64.00k" ++ ++lvremove -ff $vg ++ ++ ++### Check also lvconvert ### ++ ++lvcreate -an -L250T -n pool $vg ++ ++fail lvconvert -y --chunksize 64 --thinpool $vg/pool ++lvconvert -y --chunksize 1M --thinpool $vg/pool ++ ++check lv_field $vg/pool chunksize "1.00m" ++ ++vgremove -ff $vg +diff --git a/test/shell/thin-resize-match.sh b/test/shell/thin-resize-match.sh +index c78842f..46b4531 100644 +--- a/test/shell/thin-resize-match.sh ++++ b/test/shell/thin-resize-match.sh +@@ -12,13 +12,12 @@ + # ensure there is no data loss during thin-pool resize + + SKIP_WITH_LVMLOCKD=1 ++SKIP_WITH_LVMPOLLD=1 + + export LVM_TEST_THIN_REPAIR_CMD=${LVM_TEST_THIN_REPAIR_CMD-/bin/false} + + . lib/inittest + +-test -e LOCAL_LVMPOLLD && skip +- + which md5sum || skip + + aux have_thin 1 0 0 || skip +diff --git a/test/shell/unknown-segment.sh b/test/shell/unknown-segment.sh +index 27cea88..20fc920 100644 +--- a/test/shell/unknown-segment.sh ++++ b/test/shell/unknown-segment.sh +@@ -18,9 +18,10 @@ aux prepare_vg 4 + + lvcreate -an -Zn -l 1 -n $lv1 $vg + lvcreate -an -Zn -l 2 --type mirror -m 1 -n $lv2 $vg ++lvcreate -an -Zn --type zero -l 1 -n $lv3 $vg + + vgcfgbackup -f bak0 $vg +-sed -e 's,striped,unstriped,;s,mirror,unmirror,' -i.orig bak0 ++sed -e 's,striped,unstriped,;s,mirror,unmirror,;s,zero,zero+NEWFLAG,' -i.orig bak0 + vgcfgrestore -f bak0 $vg + + # we have on-disk metadata with unknown segments now +@@ -28,11 +29,13 @@ not lvchange -aey $vg/$lv1 # check that activation is refused + + # try once more to catch invalid memory access with valgrind + # when clvmd flushes cmd mem pool +-not lvchange -aey $vg/$lv1 # check that activation is refused ++not lvchange -aey $vg/$lv2 # check that activation is refused ++ ++not lvchange -aey $vg/$lv3 # check that activation is refused + + vgcfgbackup -f bak1 $vg + cat bak1 +-sed -e 's,unstriped,striped,;s,unmirror,mirror,' -i.orig bak1 ++sed -e 's,unstriped,striped,;s,unmirror,mirror,;s,zero+NEWFLAG,zero,' -i.orig bak1 + vgcfgrestore -f bak1 $vg + vgcfgbackup -f bak2 $vg + +diff --git a/test/unit/Makefile.in b/test/unit/Makefile.in +index 70b7e79..7aa180f 100644 +--- a/test/unit/Makefile.in ++++ b/test/unit/Makefile.in +@@ -1,4 +1,4 @@ +-# Copyright (C) 2011-2015 Red Hat, Inc. All rights reserved. ++# Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved. + # + # This file is part of LVM2. + # +@@ -24,15 +24,15 @@ UNITS = \ + string_t.c\ + run.c + +-include $(top_builddir)/make.tmpl +- +-ifeq ($(MAKECMDGOALS),distclean) ++ifeq ("@TESTING@", "yes") + SOURCES = $(UNITS) ++TARGETS = run + endif + +-ifeq ("$(TESTING)", "yes") ++include $(top_builddir)/make.tmpl ++ ++ifeq ($(MAKECMDGOALS),distclean) + SOURCES = $(UNITS) +-TARGETS = run + endif + + ifeq ("$(TESTING)", "yes") +@@ -42,6 +42,8 @@ CFLAGS += @CUNIT_CFLAGS@ + check: unit + + $(TARGETS): $(OBJECTS) $(top_builddir)/libdm/libdevmapper.$(LIB_SUFFIX) ++ $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) -L$(top_builddir)/libdm \ ++ -o $@ $(OBJECTS) $(LDLIBS) + + unit: $(TARGETS) + @echo Running unit tests +diff --git a/tools/args.h b/tools/args.h +index 87b33e7..f5e864f 100644 +--- a/tools/args.h ++++ b/tools/args.h +@@ -42,7 +42,7 @@ arg(activationmode_ARG, '\0', "activationmode", activationmode_VAL, 0, 0, + + arg(addtag_ARG, '\0', "addtag", tag_VAL, ARG_GROUPABLE, 0, + "Adds a tag to a PV, VG or LV. This option can be repeated to add\n" +- "multiple tags at once. See lvm(8) for information about tags.\n") ++ "multiple tags at once. See \\fBlvm\\fP(8) for information about tags.\n") + + arg(aligned_ARG, '\0', "aligned", 0, 0, 0, + "Use with --separator to align the output columns\n") +@@ -132,10 +132,41 @@ arg(config_ARG, '\0', "config", string_VAL, 0, 0, + "See \\fBlvm.conf\\fP(5) for more information about config.\n") + + arg(configreport_ARG, '\0', "configreport", configreport_VAL, ARG_GROUPABLE, 1, +- "See lvmreport(7).\n") ++ "See \\fBlvmreport\\fP(7).\n") + + arg(configtype_ARG, '\0', "typeconfig", configtype_VAL, 0, 0, +- "See lvmreport(7).\n") ++ "\\fBcurrent\\fP prints the config settings that would be applied\n" ++ "to an lvm command (assuming the command does not override them\n" ++ "on the command line.) This includes:\n" ++ "settings that have been modified in lvm config files,\n" ++ "settings that get their default values from config files,\n" ++ "and default settings that have been uncommented in config files.\n" ++ "\\fBdefault\\fP prints all settings with their default values.\n" ++ "Changes made in lvm config files are not reflected in the output.\n" ++ "Some settings get their default values internally,\n" ++ "and these settings are printed as comments.\n" ++ "Other settings get their default values from config files,\n" ++ "and these settings are not printed as comments.\n" ++ "\\fBdiff\\fP prints only config settings that have been modified\n" ++ "from their default values in config files (the difference between\n" ++ "current and default.)\n" ++ "\\fBfull\\fP prints every setting uncommented and set to the\n" ++ "current value, i.e. how it would be used by an lvm command.\n" ++ "This includes settings modified in config files, settings that usually\n" ++ "get defaults internally, and settings that get defaults from config files.\n" ++ "\\fBlist\\fP prints all config names without values.\n" ++ "\\fBmissing\\fP prints settings that are missing from the\n" ++ "lvm config files. A missing setting that usually gets its default\n" ++ "from config files is printed uncommented and set to the internal default.\n" ++ "Settings that get their default internally and are not set in config files\n" ++ "are printed commented with the internal default.\n" ++ "\\fBnew\\fP prints config settings that have been added since\n" ++ "the lvm version specified by --sinceversion. They are printed\n" ++ "with their default values.\n" ++ "\\fBprofilable\\fP prints settings with their default values that can be set from a profile.\n" ++ "\\fBprofilable-command\\fP prints settings with their default values that can be set from a command profile.\n" ++ "\\fBprofilable-metadata\\fP prints settings with their default values that can be set from a metadata profile.\n" ++ "Also see \\fBlvm.conf\\fP(5).\n") + + arg(dataalignment_ARG, '\0', "dataalignment", sizekb_VAL, 0, 0, + "Align the start of the data to a multiple of this number.\n" +@@ -149,7 +180,7 @@ arg(dataalignmentoffset_ARG, '\0', "dataalignmentoffset", sizekb_VAL, 0, 0, + + arg(deltag_ARG, '\0', "deltag", tag_VAL, ARG_GROUPABLE, 0, + "Deletes a tag from a PV, VG or LV. This option can be repeated to delete\n" +- "multiple tags at once. See lvm(8) for information about tags.\n") ++ "multiple tags at once. See \\fBlvm\\fP(8) for information about tags.\n") + + arg(detachprofile_ARG, '\0', "detachprofile", 0, 0, 0, + "Detaches a metadata profile from a VG or LV.\n" +@@ -188,7 +219,7 @@ arg(force_long_ARG, '\0', "force", 0, ARG_COUNTABLE, 0, + + arg(foreign_ARG, '\0', "foreign", 0, 0, 0, + "Report/display foreign VGs that would otherwise be skipped.\n" +- "See lvmsystemid(7) for more information about foreign VGs.\n") ++ "See \\fBlvmsystemid\\fP(7) for more information about foreign VGs.\n") + + arg(handlemissingpvs_ARG, '\0', "handlemissingpvs", 0, 0, 0, + "Allows a polling operation to continue when PVs are missing,\n" +@@ -198,7 +229,10 @@ arg(ignoreadvanced_ARG, '\0', "ignoreadvanced", 0, 0, 0, + "Exclude advanced configuration settings from the output.\n") + + arg(ignorelocal_ARG, '\0', "ignorelocal", 0, 0, 0, +- "Ignore local section.\n") ++ "Ignore the local section. The local section should be defined in\n" ++ "the lvmlocal.conf file, and should contain config settings\n" ++ "specific to the local host which should not be copied to\n" ++ "other hosts.\n") + + arg(ignorelockingfailure_ARG, '\0', "ignorelockingfailure", 0, 0, 0, + "Allows a command to continue with read-only metadata\n" +@@ -227,25 +261,25 @@ arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0, + + arg(lockopt_ARG, '\0', "lockopt", string_VAL, 0, 0, + "Used to pass options for special cases to lvmlockd.\n" +- "See lvmlockd(8) for more information.\n") ++ "See \\fBlvmlockd\\fP(8) for more information.\n") + + arg(lockstart_ARG, '\0', "lockstart", 0, 0, 0, + "Start the lockspace of a shared VG in lvmlockd.\n" + "lvmlockd locks becomes available for the VG, allowing LVM to use the VG.\n" +- "See lvmlockd(8) for more information.\n") ++ "See \\fBlvmlockd\\fP(8) for more information.\n") + + arg(lockstop_ARG, '\0', "lockstop", 0, 0, 0, + "Stop the lockspace of a shared VG in lvmlockd.\n" + "lvmlockd locks become unavailable for the VG, preventing LVM from using the VG.\n" +- "See lvmlockd(8) for more information.\n") ++ "See \\fBlvmlockd\\fP(8) for more information.\n") + + arg(locktype_ARG, '\0', "locktype", locktype_VAL, 0, 0, + "#vgchange\n" + "Change the VG lock type to or from a shared lock type used with lvmlockd.\n" +- "See lvmlockd(8) for more information.\n" ++ "See \\fBlvmlockd\\fP(8) for more information.\n" + "#vgcreate\n" + "Specify the VG lock type directly in place of using --shared.\n" +- "See lvmlockd(8) for more information.\n") ++ "See \\fBlvmlockd\\fP(8) for more information.\n") + + arg(logonly_ARG, '\0', "logonly", 0, 0, 0, + "Suppress command report and display only log report.\n") +@@ -345,7 +379,7 @@ arg(monitor_ARG, '\0', "monitor", bool_VAL, 0, 0, + "Start (yes) or stop (no) monitoring an LV with dmeventd.\n" + "dmeventd monitors kernel events for an LV, and performs\n" + "automated maintenance for the LV in reponse to specific events.\n" +- "See dmeventd(8) for more information.\n") ++ "See \\fBdmeventd\\fP(8) for more information.\n") + + arg(nameprefixes_ARG, '\0', "nameprefixes", 0, 0, 0, + "Add an \"LVM2_\" prefix plus the field name to the output. Useful\n" +@@ -552,7 +586,7 @@ arg(shared_ARG, '\0', "shared", 0, 0, 0, + "manager is running. This allows multiple hosts to share a VG on shared\n" + "devices. lvmlockd and a lock manager must be configured and running.\n" + "(A shared VG using lvmlockd is different from a clustered VG using clvmd.)\n" +- "See lvmlockd(8) for more information about shared VGs.\n" ++ "See \\fBlvmlockd\\fP(8) for more information about shared VGs.\n" + "#vgs\n" + "#lvs\n" + "#pvs\n" +@@ -562,7 +596,7 @@ arg(shared_ARG, '\0', "shared", 0, 0, 0, + "#pvdisplay\n" + "Report/display shared VGs that would otherwise be skipped when\n" + "lvmlockd is not being used on the host.\n" +- "See lvmlockd(8) for more information about shared VGs.\n") ++ "See \\fBlvmlockd\\fP(8) for more information about shared VGs.\n") + + arg(sinceversion_ARG, '\0', "sinceversion", string_VAL, 0, 0, + "Specify an LVM version in x.y.z format where x is the major version,\n" +@@ -644,12 +678,12 @@ arg(systemid_ARG, '\0', "systemid", string_VAL, 0, 0, + "the host creating it. Using this option requires caution because the\n" + "system ID of the new VG may not match the system ID of the host running\n" + "the command, leaving the VG inaccessible to the host.\n" +- "See lvmsystemid(7) for more information.\n" ++ "See \\fBlvmsystemid\\fP(7) for more information.\n" + "#vgchange\n" + "Changes the system ID of the VG. Using this option requires caution\n" + "because the VG may become foreign to the host running the command,\n" + "leaving the host unable to access it.\n" +- "See lvmsystemid(7) for more information.\n") ++ "See \\fBlvmsystemid\\fP(7) for more information.\n") + + arg(thinpool_ARG, '\0', "thinpool", lv_VAL, 0, 0, + "The name of a thin pool LV.\n") +@@ -808,6 +842,9 @@ arg(metadatacopies_ARG, '\0', "metadatacopies", metadatacopies_VAL, 0, 0, NULL) + * ... and now the short args. + */ + arg(activate_ARG, 'a', "activate", activation_VAL, 0, 0, ++ "#pvscan\n" ++ "Auto-activate LVs in a VG when the PVs scanned have completed the VG.\n" ++ "(Only \\fBay\\fP is applicable.)\n" + "#lvchange\n" + "#vgchange\n" + "Change the active state of LVs.\n" +@@ -828,8 +865,8 @@ arg(activate_ARG, 'a', "activate", activation_VAL, 0, 0, + "if the list is set but empty, no LVs match.\n" + "Autoactivation should be used during system boot to make it possible\n" + "to select which LVs should be automatically activated by the system.\n" +- "See lvmlockd(8) for more information about activation options \\fBey\\fP and \\fBsy\\fP for shared VGs.\n" +- "See clvmd(8) for more information about activation options \\fBey\\fP, \\fBsy\\fP, \\fBly\\fP and \\fBln\\fP for clustered VGs.\n" ++ "See \\fBlvmlockd\\fP(8) for more information about activation options \\fBey\\fP and \\fBsy\\fP for shared VGs.\n" ++ "See \\fBclvmd\\fP(8) for more information about activation options \\fBey\\fP, \\fBsy\\fP, \\fBly\\fP and \\fBln\\fP for clustered VGs.\n" + "#lvcreate\n" + "Controls the active state of the new LV.\n" + "\\fBy\\fP makes the LV active, or available.\n" +@@ -842,12 +879,16 @@ arg(activate_ARG, 'a', "activate", activation_VAL, 0, 0, + "If autoactivation \\fBay\\fP is used, the LV is only activated\n" + "if it matches an item in lvm.conf activation/auto_activation_volume_list.\n" + "\\fBay\\fP implies --zero n and --wipesignatures n.\n" +- "See lvmlockd(8) for more information about activation options for shared VGs.\n" +- "See clvmd(8) for more information about activation options for clustered VGs.\n") ++ "See \\fBlvmlockd\\fP(8) for more information about activation options for shared VGs.\n" ++ "See \\fBclvmd\\fP(8) for more information about activation options for clustered VGs.\n") + + arg(all_ARG, 'a', "all", 0, 0, 0, + "#vgreduce\n" + "Removes all empty PVs if none are named on the command line.\n" ++ "#pvchange\n" ++ "Change all visible PVs.\n" ++ "#vgimport\n" ++ "Import all visible VGs.\n" + "#lvscan\n" + "#lvdisplay\n" + "#lvs\n" +@@ -863,7 +904,7 @@ arg(all_ARG, 'a', "all", 0, 0, 0, + + arg(autobackup_ARG, 'A', "autobackup", bool_VAL, 0, 0, + "Specifies if metadata should be backed up automatically after a change.\n" +- "Enabling this is strongly advised! See vgcfgbackup(8) for more information.\n") ++ "Enabling this is strongly advised! See \\fBvgcfgbackup\\fP(8) for more information.\n") + + arg(activevolumegroups_ARG, 'A', "activevolumegroups", 0, 0, 0, + "Only select active VGs. The VG is considered active\n" +@@ -907,20 +948,20 @@ arg(clustered_ARG, 'c', "clustered", bool_VAL, 0, 0, + "This allows multiple hosts to share a VG on shared devices.\n" + "clvmd and a lock manager must be configured and running.\n" + "(A clustered VG using clvmd is different from a shared VG using lvmlockd.)\n" +- "See clvmd(8) for more information about clustered VGs.\n" ++ "See \\fBclvmd\\fP(8) for more information about clustered VGs.\n" + "#vgchange\n" + "Change the clustered property of a VG using clvmd.\n" +- "See clvmd(8) for more information about clustered VGs.\n" ++ "See \\fBclvmd\\fP(8) for more information about clustered VGs.\n" + "#vgsplit\n" + "Specifies the clustered property of the new VG.\n") + + arg(colon_ARG, 'c', "colon", 0, 0, 0, + "Generate colon separated output for easier parsing in scripts or programs.\n" +- "Also see vgs(8) which provides considerably more control over the output.\n") ++ "Also see \\fBvgs\\fP(8) which provides considerably more control over the output.\n") + + arg(columns_ARG, 'C', "columns", 0, 0, 0, +- "Display output in columns, the equivalent of vgs(8).\n" +- "Options listed are the same as options given in vgs(8).\n") ++ "Display output in columns, the equivalent of \\fBvgs\\fP(8).\n" ++ "Options listed are the same as options given in \\fBvgs\\fP(8).\n") + + arg(contiguous_ARG, 'C', "contiguous", bool_VAL, 0, 0, + "Sets or resets the contiguous allocation policy for LVs.\n" +@@ -1208,7 +1249,7 @@ arg(options_ARG, 'o', "options", string_VAL, ARG_GROUPABLE, 0, + "\\fBseg_all\\fP all LV segment fields, and\n" + "\\fBpvseg_all\\fP all PV segment columns.\n" + "See the lvm.conf report section for more config options.\n" +- "See lvmreport(7) for more information about reporting.\n") ++ "See \\fBlvmreport\\fP(7) for more information about reporting.\n") + + arg(sort_ARG, 'O', "sort", string_VAL, ARG_GROUPABLE, 0, + "Comma-separated ordered list of columns to sort by. Replaces the default\n" +@@ -1348,11 +1389,11 @@ arg(uuidstr_ARG, 'u', "uuid", string_VAL, 0, 0, + "Specify a UUID for the device.\n" + "Without this option, a random UUID is generated.\n" + "This option is needed before restoring a backup of LVM metadata\n" +- "onto a replacement device; see vgcfgrestore(8). As such, use of\n" ++ "onto a replacement device; see \\fBvgcfgrestore\\fP(8). As such, use of\n" + "--restorefile is compulsory unless the --norestorefile is used.\n" + "All PVs must have unique UUIDs, and LVM will prevent certain operations\n" + "if multiple devices are seen with the same UUID.\n" +- "See vgimportclone(8) for more information.\n") ++ "See \\fBvgimportclone\\fP(8) for more information.\n") + + /* Not used */ + arg(uuidlist_ARG, 'U', "uuidlist", 0, 0, 0, NULL) +diff --git a/tools/command-lines.in b/tools/command-lines.in +index 2d9e48e..7f9c31f 100644 +--- a/tools/command-lines.in ++++ b/tools/command-lines.in +@@ -1417,7 +1417,7 @@ DESC: Display PV information. + + pvscan --cache_long + OO: --ignorelockingfailure, --reportformat ReportFmt, --background, +---activate Active, --major Number, --minor Number ++--activate ay, --major Number, --minor Number + OP: PV|String ... + ID: pvscan_cache + DESC: Populate the lvmetad cache by scanning PVs. +diff --git a/tools/command.c b/tools/command.c +index 0f69bbb..a08d068 100644 +--- a/tools/command.c ++++ b/tools/command.c +@@ -40,6 +40,8 @@ + */ + #ifdef MAN_PAGE_GENERATOR + ++#define stack ++ + struct cmd_context { + void *libmem; + }; +@@ -699,9 +701,16 @@ static void set_opt_def(struct cmd_context *cmdtool, struct command *cmd, char * + if (val_enum == constnum_VAL) + def->num = (uint64_t)atoi(name); + +- if (val_enum == conststr_VAL) ++ if (val_enum == conststr_VAL) { + def->str = dm_pool_strdup(cmdtool->libmem, name); + ++ if (!def->str) { ++ /* FIXME */ ++ stack; ++ return; ++ } ++ } ++ + if (val_enum == lv_VAL) { + if (strchr(name, '_')) + def->lvt_bits = lv_to_bits(cmd, name); +@@ -1416,6 +1425,12 @@ int define_commands(struct cmd_context *cmdtool, const char *run_name) + cmd_count++; + cmd->name = dm_pool_strdup(cmdtool->libmem, name); + ++ if (!cmd->name) { ++ /* FIXME */ ++ stack; ++ return 0; ++ } ++ + if (run_name && strcmp(run_name, name)) { + skip = 1; + prev_was_oo_def = 0; +@@ -1447,6 +1462,10 @@ int define_commands(struct cmd_context *cmdtool, const char *run_name) + memset(newdesc, 0, newlen); + snprintf(newdesc, newlen, "%s %s", cmd->desc, desc); + cmd->desc = newdesc; ++ } else { ++ /* FIXME */ ++ stack; ++ return 0; + } + } else + cmd->desc = desc; +@@ -1465,6 +1484,12 @@ int define_commands(struct cmd_context *cmdtool, const char *run_name) + + if (is_id_line(line_argv[0]) && cmd) { + cmd->command_id = dm_pool_strdup(cmdtool->libmem, line_argv[1]); ++ ++ if (!cmd->command_id) { ++ /* FIXME */ ++ stack; ++ return 0; ++ } + continue; + } + +diff --git a/tools/lvchange.c b/tools/lvchange.c +index 660570a..92161e2 100644 +--- a/tools/lvchange.c ++++ b/tools/lvchange.c +@@ -1238,6 +1238,16 @@ static int _lvchange_properties_check(struct cmd_context *cmd, + int lv_is_named_arg) + { + if (!lv_is_visible(lv)) { ++ /* ++ * Exceptions where we allow lvchange properties on ++ * a hidden sub lv. ++ * ++ * lv_is_thin_pool_data: e.g. needed when the data sublv ++ * is a cache lv and we need to change cache properties. ++ */ ++ if (lv_is_thin_pool_data(lv)) ++ return 1; ++ + if (lv_is_named_arg) + log_error("Operation not permitted on hidden LV %s.", display_lvname(lv)); + return 0; +diff --git a/tools/lvcreate.c b/tools/lvcreate.c +index f775290..f464896 100644 +--- a/tools/lvcreate.c ++++ b/tools/lvcreate.c +@@ -251,6 +251,7 @@ static int _update_extents_params(struct volume_group *vg, + uint32_t size_rest; + uint32_t stripesize_extents; + uint32_t extents; ++ uint32_t base_calc_extents; + + if (lcp->size && + !(lp->extents = extents_from_size(vg->cmd, lcp->size, +@@ -275,17 +276,17 @@ static int _update_extents_params(struct volume_group *vg, + + switch (lcp->percent) { + case PERCENT_VG: +- extents = percent_of_extents(lp->extents, vg->extent_count, 0); ++ extents = percent_of_extents(lp->extents, base_calc_extents = vg->extent_count, 0); + break; + case PERCENT_FREE: +- extents = percent_of_extents(lp->extents, vg->free_count, 0); ++ extents = percent_of_extents(lp->extents, base_calc_extents = vg->free_count, 0); + break; + case PERCENT_PVS: + if (lcp->pv_count) { + pv_extent_count = pv_list_extents_free(lp->pvh); +- extents = percent_of_extents(lp->extents, pv_extent_count, 0); ++ extents = percent_of_extents(lp->extents, base_calc_extents = pv_extent_count, 0); + } else +- extents = percent_of_extents(lp->extents, vg->extent_count, 0); ++ extents = percent_of_extents(lp->extents, base_calc_extents = vg->extent_count, 0); + break; + case PERCENT_LV: + log_error("Please express size as %%FREE%s, %%PVS or %%VG.", +@@ -304,7 +305,7 @@ static int _update_extents_params(struct volume_group *vg, + } + /* Add whole metadata size estimation */ + extents = cow_max_extents(origin_lv, lp->chunk_size) - origin_lv->le_count + +- percent_of_extents(lp->extents, origin_lv->le_count, 1); ++ percent_of_extents(lp->extents, base_calc_extents = origin_lv->le_count, 1); + break; + case PERCENT_NONE: + extents = lp->extents; +@@ -314,10 +315,27 @@ static int _update_extents_params(struct volume_group *vg, + return 0; + } + +- if (lcp->percent) { ++ if (lcp->percent != PERCENT_NONE) { + /* FIXME Don't do the adjustment for parallel allocation with PERCENT_ORIGIN! */ + lp->approx_alloc = 1; +- log_verbose("Converted %" PRIu32 "%%%s into %" PRIu32 " extents.", lp->extents, get_percent_string(lcp->percent), extents); ++ if (!extents) { ++ log_error("Calculated size of logical volume is 0 extents. Needs to be larger."); ++ return 0; ++ } ++ ++ /* For mirrors and raid with percentages based on physical extents, convert the total number of PEs ++ * into the number of logical extents per image (minimum 1) */ ++ /* FIXME Handle all the supported raid layouts here based on already-known segtype. */ ++ if ((lcp->percent != PERCENT_ORIGIN) && lp->mirrors) { ++ extents /= lp->mirrors; ++ if (!extents) ++ extents = 1; ++ } ++ ++ log_verbose("Converted %" PRIu32 "%% of %s (%" PRIu32 ") extents into %" PRIu32 " (with mimages %" PRIu32 " and stripes %" PRIu32 ++ " for segtype %s).", lp->extents, get_percent_string(lcp->percent), base_calc_extents, ++ extents, lp->mirrors, lp->stripes, lp->segtype->name); ++ + lp->extents = extents; + } + +@@ -389,6 +407,11 @@ static int _update_extents_params(struct volume_group *vg, + } + } + ++ if ((lcp->percent != PERCENT_NONE) && !lp->extents) { ++ log_error("Adjusted size of logical volume is 0 extents. Needs to be larger."); ++ return 0; ++ } ++ + return 1; + } + +@@ -1170,6 +1193,13 @@ static int _determine_cache_argument(struct volume_group *vg, + lp->create_pool = 0; + lp->origin_name = NULL; + } else if (lv) { ++ if (arg_is_set(cmd, cachepool_ARG)) { ++ /* Argument of --cachepool has to be a cache-pool */ ++ log_error("Logical volume %s is not a cache pool.", ++ display_lvname(lv)); ++ return 0; ++ } ++ + /* Origin exists, create cache pool volume */ + if (!validate_lv_cache_create_origin(lv)) + return_0; +diff --git a/tools/reporter.c b/tools/reporter.c +index 980f39c..b9c4153 100644 +--- a/tools/reporter.c ++++ b/tools/reporter.c +@@ -172,7 +172,7 @@ static int _do_lvs_with_info_and_status_single(struct cmd_context *cmd, + if (lv_is_merging_origin(lv)) { + if (!_check_merging_origin(lv, &status, &merged)) + goto_out; +- if (merged) ++ if (merged && lv_is_thin_volume(lv->snapshot->lv)) + lv = lv->snapshot->lv; + } + +@@ -234,13 +234,13 @@ static int _do_segs_with_info_and_status_single(struct cmd_context *cmd, + if (lv_is_merging_origin(seg->lv)) { + if (!_check_merging_origin(seg->lv, &status, &merged)) + goto_out; +- if (merged) ++ if (merged && lv_is_thin_volume(seg->lv->snapshot->lv)) + seg = seg->lv->snapshot; + } + + if (!report_object(sh ? : handle->custom_handle, sh != NULL, + seg->lv->vg, seg->lv, NULL, seg, NULL, &status, NULL)) +- goto_out; ++ goto_out; + + r = ECMD_PROCESSED; + out: +diff --git a/tools/toollib.c b/tools/toollib.c +index f3cf4ca..e5a854f 100644 +--- a/tools/toollib.c ++++ b/tools/toollib.c +@@ -1335,9 +1335,17 @@ int get_stripe_params(struct cmd_context *cmd, const struct segment_type *segtyp + return _validate_stripe_params(cmd, segtype, stripes, stripe_size); + } + +-static int _validate_cachepool_params(const char *name, +- const struct dm_config_tree *settings) ++static int _validate_cachepool_params(const char *policy_name, cache_mode_t cache_mode) + { ++ /* ++ * FIXME: it might be nice if cmd def rules could check option values, ++ * then a rule could do this. ++ */ ++ if ((cache_mode == CACHE_MODE_WRITEBACK) && policy_name && !strcmp(policy_name, "cleaner")) { ++ log_error("Cache mode \"writeback\" is not compatible with cache policy \"cleaner\"."); ++ return 0; ++ } ++ + return 1; + } + +@@ -1371,6 +1379,9 @@ int get_cache_params(struct cmd_context *cmd, + + *name = arg_str_value(cmd, cachepolicy_ARG, NULL); + ++ if (!_validate_cachepool_params(*name, *cache_mode)) ++ goto_out; ++ + dm_list_iterate_items(group, &cmd->arg_value_groups) { + if (!grouped_arg_is_set(group->arg_values, cachesettings_ARG)) + continue; +@@ -1401,9 +1412,6 @@ int get_cache_params(struct cmd_context *cmd, + cn->child = result->root; + result->root = cn; + } +- +- if (!_validate_cachepool_params(*name, result)) +- goto_out; + } + + ok = 1; diff --git a/SOURCES/lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch b/SOURCES/lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch index 949983d..028db8e 100644 --- a/SOURCES/lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch +++ b/SOURCES/lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch @@ -4,10 +4,10 @@ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/configure b/configure -index 8253bfc..587b978 100755 +index ffef9b9..1e4e744 100755 --- a/configure +++ b/configure -@@ -11951,12 +11951,12 @@ if test -n "$BLKID_CFLAGS"; then +@@ -12246,12 +12246,12 @@ if test -n "$BLKID_CFLAGS"; then pkg_cv_BLKID_CFLAGS="$BLKID_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ @@ -23,7 +23,7 @@ index 8253bfc..587b978 100755 test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes -@@ -11968,12 +11968,12 @@ if test -n "$BLKID_LIBS"; then +@@ -12263,12 +12263,12 @@ if test -n "$BLKID_LIBS"; then pkg_cv_BLKID_LIBS="$BLKID_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ @@ -39,7 +39,7 @@ index 8253bfc..587b978 100755 test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes -@@ -11994,9 +11994,9 @@ else +@@ -12289,9 +12289,9 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then @@ -51,7 +51,7 @@ index 8253bfc..587b978 100755 fi # Put the nasty error message in config.log where it belongs echo "$BLKID_PKG_ERRORS" >&5 -@@ -12004,7 +12004,7 @@ fi +@@ -12299,7 +12299,7 @@ fi if test "$BLKID_WIPING" = maybe; then BLKID_WIPING=no else @@ -60,7 +60,7 @@ index 8253bfc..587b978 100755 fi elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -@@ -12012,7 +12012,7 @@ $as_echo "no" >&6; } +@@ -12307,7 +12307,7 @@ $as_echo "no" >&6; } if test "$BLKID_WIPING" = maybe; then BLKID_WIPING=no else @@ -70,10 +70,10 @@ index 8253bfc..587b978 100755 else BLKID_CFLAGS=$pkg_cv_BLKID_CFLAGS diff --git a/configure.in b/configure.in -index 33b5c76..9ed6226 100644 +index 2ff4f99..7d2baa0 100644 --- a/configure.in +++ b/configure.in -@@ -1302,12 +1302,12 @@ AC_MSG_RESULT($BLKID_WIPING) +@@ -1319,12 +1319,12 @@ AC_MSG_RESULT($BLKID_WIPING) if test "$BLKID_WIPING" != no; then pkg_config_init @@ -89,10 +89,10 @@ index 33b5c76..9ed6226 100644 if test "$BLKID_WIPING" = yes; then BLKID_PC="blkid" diff --git a/lib/device/dev-type.c b/lib/device/dev-type.c -index 0246c09..bae984a 100644 +index 325b66b..cf7f992 100644 --- a/lib/device/dev-type.c +++ b/lib/device/dev-type.c -@@ -703,8 +703,7 @@ static int _wipe_known_signatures_with_blkid(struct device *dev, const char *nam +@@ -713,8 +713,7 @@ static int _wipe_known_signatures_with_blkid(struct device *dev, const char *nam BLKID_SUBLKS_TYPE | BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION | diff --git a/SOURCES/lvm2-revert-fix-for-lvconvert-repair-for-raid-lvs.patch b/SOURCES/lvm2-revert-fix-for-lvconvert-repair-for-raid-lvs.patch deleted file mode 100644 index 77869ae..0000000 --- a/SOURCES/lvm2-revert-fix-for-lvconvert-repair-for-raid-lvs.patch +++ /dev/null @@ -1,323 +0,0 @@ - WHATS_NEW | 1 - - daemons/dmeventd/plugins/raid/dmeventd_raid.c | 42 +++-------- - lib/metadata/lv.c | 7 ++ - lib/metadata/raid_manip.c | 2 +- - test/shell/lvconvert-repair-raid.sh | 104 ++------------------------ - tools/lvconvert.c | 19 +++++ - 6 files changed, 41 insertions(+), 134 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 25f6742..977e578 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -3,7 +3,6 @@ Version 2.02.166 - 26th September 2016 - Fix lvm2-activation-generator to read all LVM2 config sources. (2.02.155) - Fix lvchange-rebuild-raid.sh to cope with older target versions. - Use dm_config_parse_without_dup_node_check() to speedup metadata reading. -- Fix lvconvert --repair regression - Fix reported origin lv field for cache volumes. (2.02.133) - Always specify snapshot cow LV for monitoring not internal LV. (2.02.165) - Fix lvchange --discard|--zero for active thin-pool. -diff --git a/daemons/dmeventd/plugins/raid/dmeventd_raid.c b/daemons/dmeventd/plugins/raid/dmeventd_raid.c -index bec594a..770fbc6 100644 ---- a/daemons/dmeventd/plugins/raid/dmeventd_raid.c -+++ b/daemons/dmeventd/plugins/raid/dmeventd_raid.c -@@ -1,5 +1,5 @@ - /* -- * Copyright (C) 2005-2016 Red Hat, Inc. All rights reserved. -+ * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved. - * - * This file is part of LVM2. - * -@@ -13,20 +13,14 @@ - */ - - #include "lib.h" --#include "defaults.h" - #include "dmeventd_lvm.h" - #include "libdevmapper-event.h" - --/* Hold enough elements for the mximum number of RAID images */ --#define RAID_DEVS_ELEMS ((DEFAULT_RAID_MAX_IMAGES + 63) / 64) -- - struct dso_state { - struct dm_pool *mem; - char cmd_lvscan[512]; - char cmd_lvconvert[512]; -- uint64_t raid_devs[RAID_DEVS_ELEMS]; - int failed; -- int warned; - }; - - DM_EVENT_LOG_FN("raid") -@@ -37,39 +31,20 @@ static int _process_raid_event(struct dso_state *state, char *params, const char - { - struct dm_status_raid *status; - const char *d; -- int dead = 0, r = 1; - - if (!dm_get_status_raid(state->mem, params, &status)) { - log_error("Failed to process status line for %s.", device); - return 0; - } - -- d = status->dev_health; -- while ((d = strchr(d, 'D'))) { -- uint32_t dev = (uint32_t)(d - status->dev_health); -- -- if (!(state->raid_devs[dev / 64] & (1 << (dev % 64)))) -- log_error("Device #%u of %s array, %s, has failed.", -- dev, status->raid_type, device); -- -- state->raid_devs[dev / 64] |= (1 << (dev % 64)); -- d++; -- dead = 1; -- } -- -- if (dead) { -- if (status->insync_regions < status->total_regions) { -- if (!state->warned) -- log_warn("WARNING: waiting for resynchronization to finish " -- "before initiating repair on RAID device %s", device); -- -- state->warned = 1; -- goto out; /* Not yet done syncing with accessible devices */ -- } -- -+ if ((d = strchr(status->dev_health, 'D'))) { - if (state->failed) - goto out; /* already reported */ - -+ log_error("Device #%d of %s array, %s, has failed.", -+ (int)(d - status->dev_health), -+ status->raid_type, device); -+ - state->failed = 1; - if (!dmeventd_lvm2_run_with_lock(state->cmd_lvscan)) - log_warn("WARNING: Re-scan of RAID device %s failed.", device); -@@ -77,7 +52,8 @@ static int _process_raid_event(struct dso_state *state, char *params, const char - /* if repair goes OK, report success even if lvscan has failed */ - if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) { - log_info("Repair of RAID device %s failed.", device); -- r = 0; -+ dm_pool_free(state->mem, status); -+ return 0; - } - } else { - state->failed = 0; -@@ -88,7 +64,7 @@ static int _process_raid_event(struct dso_state *state, char *params, const char - out: - dm_pool_free(state->mem, status); - -- return r; -+ return 1; - } - - void process_event(struct dm_task *dmt, -diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c -index 70036f9..53a1044 100644 ---- a/lib/metadata/lv.c -+++ b/lib/metadata/lv.c -@@ -988,6 +988,7 @@ int lv_mirror_image_in_sync(const struct logical_volume *lv) - int lv_raid_image_in_sync(const struct logical_volume *lv) - { - unsigned s; -+ dm_percent_t percent; - char *raid_health; - struct lv_segment *seg, *raid_seg = NULL; - -@@ -1017,6 +1018,12 @@ int lv_raid_image_in_sync(const struct logical_volume *lv) - return 0; - } - -+ if (!lv_raid_percent(raid_seg->lv, &percent)) -+ return_0; -+ -+ if (percent == DM_PERCENT_100) -+ return 1; -+ - /* Find out which sub-LV this is. */ - for (s = 0; s < raid_seg->area_count; s++) - if (seg_lv(raid_seg, s) == lv) -diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c -index deb88a2..e5fdf4f 100644 ---- a/lib/metadata/raid_manip.c -+++ b/lib/metadata/raid_manip.c -@@ -3658,7 +3658,7 @@ static int _lv_raid_rebuild_or_replace(struct logical_volume *lv, - return 0; - } - -- if (!_raid_in_sync(lv)) { -+ if (!mirror_in_sync() && !_raid_in_sync(lv)) { - log_error("Unable to replace devices in %s/%s while it is" - " not in-sync.", lv->vg->name, lv->name); - return 0; -diff --git a/test/shell/lvconvert-repair-raid.sh b/test/shell/lvconvert-repair-raid.sh -index b51d8fe..1ef91c4 100644 ---- a/test/shell/lvconvert-repair-raid.sh -+++ b/test/shell/lvconvert-repair-raid.sh -@@ -22,52 +22,11 @@ aux lvmconf 'allocation/maximise_cling = 0' \ - - aux prepare_vg 8 - --function delay --{ -- for d in $(< DEVICES) -- do -- aux delay_dev "$d" 0 $1 $(get first_extent_sector "$d") -- done --} -- - # It's possible small raid arrays do have problems with reporting in-sync. - # So try bigger size --RAID_SIZE=32 -- --# Fast sync and repair afterwards --delay 0 -- --# RAID1 dual-leg single replace after initial sync --lvcreate --type raid1 -m 1 -L $RAID_SIZE -n $lv1 $vg "$dev1" "$dev2" --aux wait_for_sync $vg $lv1 --aux disable_dev "$dev2" --lvconvert -y --repair $vg/$lv1 --vgreduce --removemissing $vg --aux enable_dev "$dev2" --vgextend $vg "$dev2" --lvremove -ff $vg/$lv1 -- --# Delayed sync to allow for repair during rebuild --delay 50 -- --# RAID1 triple-leg single replace during initial sync --lvcreate --type raid1 -m 2 -L $RAID_SIZE -n $lv1 $vg "$dev1" "$dev2" "$dev3" --aux disable_dev "$dev2" "$dev3" --not lvconvert -y --repair $vg/$lv1 --aux wait_for_sync $vg $lv1 --lvconvert -y --repair $vg/$lv1 --vgreduce --removemissing $vg --aux enable_dev "$dev2" "$dev3" --vgextend $vg "$dev2" "$dev3" --lvremove -ff $vg/$lv1 -- -- --# Larger RAID size possible for striped RAID - RAID_SIZE=64 - --# Fast sync and repair afterwards --delay 0 --# RAID5 single replace after initial sync -+# RAID5 single replace - lvcreate --type raid5 -i 2 -L $RAID_SIZE -n $lv1 $vg "$dev1" "$dev2" "$dev3" - aux wait_for_sync $vg $lv1 - aux disable_dev "$dev3" -@@ -75,69 +34,16 @@ lvconvert -y --repair $vg/$lv1 - vgreduce --removemissing $vg - aux enable_dev "$dev3" - vgextend $vg "$dev3" --lvremove -ff $vg/$lv1 -+lvremove -ff $vg - --# Delayed sync to allow for repair during rebuild --delay 50 -- --# RAID5 single replace during initial sync --lvcreate --type raid5 -i 2 -L $RAID_SIZE -n $lv1 $vg "$dev1" "$dev2" "$dev3" --aux disable_dev "$dev3" --not lvconvert -y --repair $vg/$lv1 --aux wait_for_sync $vg $lv1 --lvconvert -y --repair $vg/$lv1 --vgreduce --removemissing $vg --aux enable_dev "$dev3" --vgextend $vg "$dev3" --lvremove -ff $vg/$lv1 -- --# Fast sync and repair afterwards --delay 0 -- --# RAID6 double replace after initial sync -+# RAID6 double replace - lvcreate --type raid6 -i 3 -L $RAID_SIZE -n $lv1 $vg \ - "$dev1" "$dev2" "$dev3" "$dev4" "$dev5" - aux wait_for_sync $vg $lv1 - aux disable_dev "$dev4" "$dev5" - lvconvert -y --repair $vg/$lv1 - vgreduce --removemissing $vg --aux enable_dev "$dev4" "$dev5" --vgextend $vg "$dev4" "$dev5" --lvremove -ff $vg/$lv1 -- --# Delayed sync to allow for repair during rebuild --delay 50 -- --# RAID6 single replace after initial sync --lvcreate --type raid6 -i 3 -L $RAID_SIZE -n $lv1 $vg \ -- "$dev1" "$dev2" "$dev3" "$dev4" "$dev5" --aux disable_dev "$dev4" --not lvconvert -y --repair $vg/$lv1 --delay 0 # Fast sync and repair afterwards --aux disable_dev "$dev4" # Need to disable again after changing delay --aux wait_for_sync $vg $lv1 --lvconvert -y --repair $vg/$lv1 --vgreduce --removemissing $vg - aux enable_dev "$dev4" --vgextend $vg "$dev4" --lvremove -ff $vg/$lv1 -- --# Delayed sync to allow for repair during rebuild --delay 50 -- --# RAID10 single replace after initial sync --lvcreate --type raid10 -m 1 -i 2 -L $RAID_SIZE -n $lv1 $vg \ -- "$dev1" "$dev2" "$dev3" "$dev4" --aux disable_dev "$dev4" --not lvconvert -y --repair $vg/$lv1 --delay 0 # Fast sync and repair afterwards --aux disable_dev "$dev4" # Need to disable again after changing delay --aux disable_dev "$dev1" --aux wait_for_sync $vg $lv1 --lvconvert -y --repair $vg/$lv1 --vgreduce --removemissing $vg --aux enable_dev "$dev4" --vgextend $vg "$dev4" --lvremove -ff $vg/$lv1 -- -+aux enable_dev "$dev5" -+vgextend $vg "$dev4" "$dev5" - vgremove -ff $vg -diff --git a/tools/lvconvert.c b/tools/lvconvert.c -index 3607247..d1d21b6 100644 ---- a/tools/lvconvert.c -+++ b/tools/lvconvert.c -@@ -1826,6 +1826,7 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l - struct dm_list *failed_pvs; - struct cmd_context *cmd = lv->vg->cmd; - struct lv_segment *seg = first_seg(lv); -+ dm_percent_t sync_percent; - - if (_linear_type_requested(lp->type_str)) { - if (arg_is_set(cmd, mirrors_ARG) && (arg_uint_value(cmd, mirrors_ARG, 0) != 0)) { -@@ -1973,6 +1974,24 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l - return 0; - } - -+ if (!lv_raid_percent(lv, &sync_percent)) { -+ log_error("Unable to determine sync status of %s.", -+ display_lvname(lv)); -+ return 0; -+ } -+ -+ if (sync_percent != DM_PERCENT_100) { -+ log_warn("WARNING: %s is not in-sync.", display_lvname(lv)); -+ log_warn("WARNING: Portions of the array may be unrecoverable."); -+ -+ /* -+ * The kernel will not allow a device to be replaced -+ * in an array that is not in-sync unless we override -+ * by forcing the array to be considered "in-sync". -+ */ -+ init_mirror_in_sync(1); -+ } -+ - _lvconvert_raid_repair_ask(cmd, lp, &replace); - - if (replace) { diff --git a/SOURCES/lvm2-rhel7-fix-StartLimitInterval.patch b/SOURCES/lvm2-rhel7-fix-StartLimitInterval.patch new file mode 100644 index 0000000..e5f79e2 --- /dev/null +++ b/SOURCES/lvm2-rhel7-fix-StartLimitInterval.patch @@ -0,0 +1,23 @@ + scripts/lvm2_pvscan_systemd_red_hat@.service.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/lvm2_pvscan_systemd_red_hat@.service.in b/scripts/lvm2_pvscan_systemd_red_hat@.service.in +index 0989590..9e3d19c 100644 +--- a/scripts/lvm2_pvscan_systemd_red_hat@.service.in ++++ b/scripts/lvm2_pvscan_systemd_red_hat@.service.in +@@ -2,7 +2,6 @@ + Description=LVM2 PV scan on device %i + Documentation=man:pvscan(8) + DefaultDependencies=no +-StartLimitInterval=0 + BindsTo=dev-block-%i.device + Requires=lvm2-lvmetad.socket + After=lvm2-lvmetad.socket lvm2-lvmetad.service +@@ -14,3 +13,4 @@ Type=oneshot + RemainAfterExit=yes + ExecStart=@sbindir@/lvm pvscan --cache --activate ay %i + ExecStop=@sbindir@/lvm pvscan --cache %i ++StartLimitInterval=0 +-- +1.8.3.1 + diff --git a/SOURCES/lvm2-rhel7.patch b/SOURCES/lvm2-rhel7.patch index b029dce..7bceb41 100644 --- a/SOURCES/lvm2-rhel7.patch +++ b/SOURCES/lvm2-rhel7.patch @@ -1,18 +1,18 @@ VERSION | 2 +- VERSION_DM | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION -index dd4e60e..39d6c15 100644 +index 1f63cd7..0b41bd1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ --2.02.166(2) (2016-09-26) -+2.02.166(2)-RHEL7 (2016-11-16) +-2.02.171(2) (2017-05-03) ++2.02.171(2)-RHEL7 (2017-05-03) diff --git a/VERSION_DM b/VERSION_DM -index d53f47a..005fbd4 100644 +index 030d9c2..56b50a2 100644 --- a/VERSION_DM +++ b/VERSION_DM @@ -1 +1 @@ --1.02.135 (2016-09-26) -+1.02.135-RHEL7 (2016-11-16) +-1.02.140 (2017-05-03) ++1.02.140-RHEL7 (2017-05-03) diff --git a/SOURCES/lvm2-set-default-preferred_names.patch b/SOURCES/lvm2-set-default-preferred_names.patch index 1c44605..c545662 100644 --- a/SOURCES/lvm2-set-default-preferred_names.patch +++ b/SOURCES/lvm2-set-default-preferred_names.patch @@ -3,7 +3,7 @@ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/example.conf.in b/conf/example.conf.in -index c0afcb7..ec12918 100644 +index ed34b91..b9fe1d9 100644 --- a/conf/example.conf.in +++ b/conf/example.conf.in @@ -106,7 +106,7 @@ devices { @@ -16,10 +16,10 @@ index c0afcb7..ec12918 100644 # Configuration option devices/filter. # Limit the block devices that are used by LVM commands. diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h -index 9017043..c06b6f0 100644 +index 3b0eebb..9a87e20 100644 --- a/lib/config/config_settings.h +++ b/lib/config/config_settings.h -@@ -205,7 +205,7 @@ cfg(devices_external_device_info_source_CFG, "external_device_info_source", devi +@@ -211,7 +211,7 @@ cfg(devices_external_device_info_source_CFG, "external_device_info_source", devi " compiled with udev support.\n" "#\n") diff --git a/SPECS/lvm2.spec b/SPECS/lvm2.spec index 4ca2dff..279c607 100644 --- a/SPECS/lvm2.spec +++ b/SPECS/lvm2.spec @@ -1,4 +1,4 @@ -%global device_mapper_version 1.02.135 +%global device_mapper_version 1.02.140 %global enable_cache 1 %global enable_cluster 1 @@ -8,6 +8,7 @@ %global enable_lvmpolld 1 %global enable_python 1 %global enable_thin 1 +%global enable_dmfilemapd 1 %global systemd_version 189-3 %global dracut_version 002-18 @@ -15,22 +16,22 @@ %global bash_version 4.0 %global corosync_version 1.99.9-1 %global resource_agents_version 3.9.5-25 -%global dlm_version 3.99.1-1 +%global dlm_version 4.0.6-2 %global libselinux_version 1.30.19-4 -%global persistent_data_version 0.6.3-1 +%global persistent_data_version 0.7.0-0.1.rc6 %global sanlock_version 3.3.0-1 %global enable_lockd_sanlock %{enable_lvmlockd} %global enable_lockd_dlm %{enable_lvmlockd} %if 0%{?rhel} - %ifnarch i686 x86_64 s390x + %ifnarch i686 x86_64 ppc64le s390x %global enable_cluster 0 %global enable_cmirror 0 %global enable_lockd_dlm 0 %endif - %ifnarch x86_64 ppc64le ppc64 aarch64 + %ifnarch x86_64 ppc64 aarch64 %global enable_lockd_sanlock 0 %endif %endif @@ -50,8 +51,8 @@ Summary: Userland logical volume management tools Name: lvm2 Epoch: 7 -Version: 2.02.166 -Release: 1%{?dist}.5 +Version: 2.02.171 +Release: 8%{?dist} License: GPLv2 Group: System Environment/Base URL: http://sources.redhat.com/lvm2 @@ -61,14 +62,15 @@ Patch1: lvm2-set-default-preferred_names.patch Patch2: lvm2-fix-libdm-versioning-for-dm_tree_node_size_changed-symbol.patch Patch3: lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch Patch4: lvm2-default-allow-changes-with-duplicate-pvs.patch -Patch5: lvm2-revert-fix-for-lvconvert-repair-for-raid-lvs.patch -Patch6: lvm2-2_02_167-disable-lvconvert-of-thin-pool-to-raid-while-active.patch -Patch7: lvm2-2_02_167-fix-inability-of-lvconvert-repair-for-cache-raid-volumes.patch -Patch8: lvm2-2_02_167-fix-raid4-coversion-from-striped.patch -Patch9: lvm2-2_02_167-prevent-raid4-creation-and-conversion-on-non-supporting-kernels.patch -Patch10: lvm2-2_02_169-fix-segfault-in-lvmetad-from-missing-null-in-daemon_reply_simple.patch -Patch11: lvm2-2_02_169-clvmd-add-mutex-protection-for-cpg_-call.patch -Patch12: lvm2-2_02_169-lvcreate-fix-striped-limit.patch +Patch5: lvm2-rhel7-fix-StartLimitInterval.patch +# up to commit: 6c4b2a6aa16cdb4aff2bd80909dcf43032348a3a: +Patch6: lvm2-2_02_172-upstream.patch +# up to commit: 93fbfa2ed397d7b87d4088e19303748c242f24d1 +Patch7: lvm2-2_02_172-upstream-6.patch +# upstream patch: 64fac77e8a551f4dfe8f4cfaaf1ca984c9b5146c +Patch8: lvm2-2_02_172-fix-raid-segfault.patch +# upstream patch: 4e4067dd94f52f90f0aaae30c522102ccbaa2826 +Patch9: lvm2-2_02_172-libdm-initialization-of-reused-struct.patch BuildRequires: libselinux-devel >= %{libselinux_version}, libsepol-devel BuildRequires: libblkid-devel >= %{util_linux_version} @@ -119,14 +121,11 @@ or more physical volumes and creating one or more logical volumes %patch2 -p1 -b .libdm_symbol_versioning %patch3 -p1 -b .blkid_sublks_badcsum %patch4 -p1 -b .default_allow_dup -%patch5 -p1 -b .lvconvert_repair_raid -%patch6 -p1 -b .lvconvert_disable_thin_pool_to_raid -%patch7 -p1 -b .lvconvert_cache_raid -%patch8 -p1 -b .raid4_from_striped -%patch9 -p1 -b .raid4_vs_kernel -%patch10 -p1 -b .lvmetad_segfault -%patch11 -p1 -b .ipc_ring_corruption -%patch12 -p1 -b .striped_limit +%patch5 -p1 -b .startlimitinterval +%patch6 -p1 -b .v172 +%patch7 -p1 -b .v172_6 +%patch8 -p1 -b .raid_segfault +%patch9 -p1 -b .libdm_init %build %global _default_pid_dir /run @@ -152,11 +151,11 @@ or more physical volumes and creating one or more logical volumes %endif %if %{enable_lockd_dlm} -%global configure_lockd_dlm --enable-lockd-dlm +%global configure_lockd_dlm --enable-lvmlockd-dlm %endif %if %{enable_lockd_sanlock} -%global configure_lockd_sanlock --enable-lockd-sanlock +%global configure_lockd_sanlock --enable-lvmlockd-sanlock %endif %if %{enable_lvmpolld} @@ -167,7 +166,11 @@ or more physical volumes and creating one or more logical volumes %global configure_python --enable-python2-bindings %endif -%configure --with-default-dm-run-dir=%{_default_dm_run_dir} --with-default-run-dir=%{_default_run_dir} --with-default-pid-dir=%{_default_pid_dir} --with-default-locking-dir=%{_default_locking_dir} --with-usrlibdir=%{_libdir} --enable-lvm1_fallback --enable-fsadm --with-pool=internal --enable-write_install --with-user= --with-group= --with-device-uid=0 --with-device-gid=6 --with-device-mode=0660 --enable-pkgconfig --enable-applib --enable-cmdlib --enable-dmeventd --enable-blkid_wiping %{?configure_python} %{?configure_cluster} %{?configure_cmirror} %{?configure_udev} %{?configure_thin} %{?configure_lvmetad} %{?configure_cache} %{?configure_lvmpolld} %{?configure_lockd_dlm} %{?configure_lockd_sanlock} +%if %{enable_dmfilemapd} +%global configure_dmfilemapd --enable-dmfilemapd +%endif + +%configure --with-default-dm-run-dir=%{_default_dm_run_dir} --with-default-run-dir=%{_default_run_dir} --with-default-pid-dir=%{_default_pid_dir} --with-default-locking-dir=%{_default_locking_dir} --with-usrlibdir=%{_libdir} --enable-lvm1_fallback --enable-fsadm --with-pool=internal --enable-write_install --with-user= --with-group= --with-device-uid=0 --with-device-gid=6 --with-device-mode=0660 --enable-pkgconfig --enable-applib --enable-cmdlib --enable-dmeventd --enable-blkid_wiping %{?configure_python} %{?configure_cluster} %{?configure_cmirror} %{?configure_udev} %{?configure_thin} %{?configure_lvmetad} %{?configure_cache} %{?configure_lvmpolld} %{?configure_lockd_dlm} %{?configure_lockd_sanlock} %{?configure_dmfilemapd} make %{?_smp_mflags} @@ -262,7 +265,6 @@ systemctl start lvm2-lvmpolld.socket %{_sbindir}/lvcreate %{_sbindir}/lvdisplay %{_sbindir}/lvextend -%{_sbindir}/lvmchange %{_sbindir}/lvmdiskscan %{_sbindir}/lvmsadc %{_sbindir}/lvmsar @@ -317,7 +319,6 @@ systemctl start lvm2-lvmpolld.socket %{_mandir}/man8/lvm-config.8.gz %{_mandir}/man8/lvmconfig.8.gz %{_mandir}/man8/lvm-dumpconfig.8.gz -%{_mandir}/man8/lvmchange.8.gz %{_mandir}/man8/lvmconf.8.gz %{_mandir}/man8/lvmdiskscan.8.gz %{_mandir}/man8/lvmdump.8.gz @@ -701,10 +702,14 @@ for the kernel device-mapper. %defattr(444,root,root,-) %attr(555, -, -) %{_sbindir}/dmsetup %attr(555, -, -) %{_sbindir}/blkdeactivate -%{_sbindir}/dmstats +%attr(555, -, -) %{_sbindir}/dmstats %{_mandir}/man8/dmsetup.8.gz %{_mandir}/man8/dmstats.8.gz %{_mandir}/man8/blkdeactivate.8.gz +%if %{enable_dmfilemapd} +%attr(555, -, -) %{_sbindir}/dmfilemapd +%{_mandir}/man8/dmfilemapd.8.gz +%endif %{_udevdir}/10-dm.rules %{_udevdir}/13-dm-disk.rules %{_udevdir}/95-dm-notify.rules @@ -829,12 +834,81 @@ the device-mapper event library. %{_libdir}/pkgconfig/devmapper-event.pc %changelog -* Fri Jun 09 2017 Marian Csontos - 7:2.02.166-1.el7_3.5 -- Fix limit of stripes in lvcreate. +* Wed Jun 28 2017 Marian Csontos - 7:2.02.171-8 +- Fix reusing of dm_task structure for status reading (used by dmeventd). + +* Thu Jun 22 2017 Marian Csontos - 7:2.02.171-7 +- Fix a segfault in RAID. + +* Wed Jun 21 2017 Marian Csontos - 7:2.02.171-6 +- Reenable conversion of data and metadata thin-pool volumes to raid. +- Improve handling of RAID status in lvs. +- Disable RAID reshape/take-over in a cluster or when snapshot exists. + +* Wed Jun 14 2017 Marian Csontos - 7:2.02.171-5 +- Improve lvcreate --cachepool arg validation. +- Limit maximal size of thin-pool for specific chunk size. +- Accept truncated files in calls to dm_stats_update_regions_from_fd(). + +* Wed Jun 07 2017 Marian Csontos - 7:2.02.171-4 +- Converting linear to raid1 using "recover" not "resync". +- Print a warning about in-use PVs with no VG using them. +- Disable automatic clearing of PVs that look like in-use orphans. + +* Wed Jun 07 2017 Marian Csontos - 7:2.02.171-4 +- Converting linear to raid1 using "recover" not "resync". +- Print a warning about in-use PVs with no VG using them. +- Disable automatic clearing of PVs that look like in-use orphans. + +* Wed May 31 2017 Marian Csontos - 7:2.02.171-3 +- Cache format2 flag is now using segment name type field compatible with + previous versions. + +* Wed May 24 2017 Marian Csontos - 7:2.02.171-2 +- Stop using '--yes' mode when fsadm runs without terminal. +- Extend validation of filesystems resized by fsadm. +- Enhance lvconvert automatic settings of possible (raid) LV types. +- Allow lvchange to change properties on a thin pool data sub LV. +- Fix lvcreate extent percentage calculation for mirrors. +- Don't reinstate still-missing devices when correcting inconsistent metadata. +- Properly handle subshell return codes in fsadm. +- Disallow cachepool creation with policy cleaner and mode writeback. + +* Wed May 03 2017 Marian Csontos - 7:2.02.171-1 +- Fix dmstats and dmfilemapd permissions. +- Fix memory warnings by using mempools for command definition processing. +- Fix running commands from a script file. +- Adjust pvresize/pvcreate messages and prompt if underlying device size + differs. +- Preserve region size on raid1 image count changes. +- Handle insufficient space on RAID takeover. +- Raise mirror/raid default regionsize to 2MiB. + +* Thu Apr 13 2017 Marian Csontos - 7:2.02.170-2 +- Fix RPM build - missing upstrem and sources. + +* Thu Apr 13 2017 Marian Csontos - 7:2.02.170-1 +- Introduce global/fsadm_executable to make fsadm path configurable. +- Look for limited thin pool metadata size when using 16G metadata. +- Fix missing lvmlockd LV locks in lvchange and lvconvert. +- Allow valid lvconvert --regionsize change. +- Handle insufficient PVs on lvconvert takeover. +- Fix SIGINT blocking to prevent corrupted metadata. +- Fix systemd unit existence check for lvmconf --services --startstopservices. + +* Wed Mar 29 2017 Marian Csontos - 7:2.02.169-3 +- Fix dmfilemapd's Makefile. +- Update dependencies. + +* Wed Mar 29 2017 Marian Csontos - 7:2.02.169-2 +- Enable dmfilemapd. + +* Tue Mar 28 2017 Marian Csontos - 7:2.02.169-1 +- Update to latest upstream release with various fixes and + enhancements documented in WHATS_NEW and WHATS_NEW_DM file. -* Tue Apr 04 2017 Marian Csontos - 7:2.02.166-1.el7_3.4 -- Fix stuck corosync due to unsafe thread access to CPG causing IPC ringbuffer - corruption. +* Thu Mar 23 2017 Alasdair Kergon - 7:2.02.166-2 +- Add clustered ppc64le support to build. * Thu Feb 09 2017 Peter Rajnoha - 7:2.02.166-1.el7_3.3 - Fix segfault in lvmetad from missing NULL in daemon_reply_simple. @@ -1563,7 +1637,7 @@ the device-mapper event library. - Report blank origin_size field if the LV doesn't have an origin instead of 0. - Report partial and in-sync RAID attribute based on kernel status - Log output also to syslog when abort_on_internal_error is set. -- Change lvs heading Copy% to Cpy%Sync and print RAID4/5/6 sync% there too. +- Change lvs heading Copy% to Cpy%%Sync and print RAID4/5/6 sync% there too. - Report error for nonexisting devices in dmeventd communication. - Reduce some log_error messages to log_warn where we don't fail. - o Configuration: @@ -1897,7 +1971,7 @@ the device-mapper event library. - Update man pages to give them all the same look&feel. - Fix lvresize of thin pool for striped devices. - For lvresize round upward when specifying number of extents. -- For lvcreate with %FREE support rounding downward stripe alignment. +- For lvcreate with %%FREE support rounding downward stripe alignment. - Change message severity to log_very_verbose for missing dev info in udev db. - Fix lvconvert when specifying removal of a RAID device other than last one. - Fix ability to handle failures in mirrored log in dmeventd plugin. (2.02.89)