summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WHATS_NEW1
-rw-r--r--lib/metadata/metadata-exported.h1
-rw-r--r--lib/metadata/mirror.c54
-rw-r--r--tools/vgreduce.c5
4 files changed, 54 insertions, 7 deletions
diff --git a/WHATS_NEW b/WHATS_NEW
index 204acb1b..055ce7ab 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
Version 2.02.40 -
================================
+ Avoid shuffling remaining mirror images when removing one, retaining primary.
Add missing LV error target activation in _remove_mirror_images.
Prevent resizing an LV while lvconvert is using it.
Avoid repeatedly wiping cache while VG_GLOBAL is held in vgscan & pvscan.
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index e42597a2..a5390595 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -532,6 +532,7 @@ int add_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors,
struct list *removable_pvs, unsigned remove_log);
int collapse_mirrored_lv(struct logical_volume *lv);
+int shift_mirror_images(struct lv_segment *mirrored_seg, unsigned mimage);
struct logical_volume *find_pvmove_lv(struct volume_group *vg,
struct device *dev, uint32_t lv_type);
diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c
index 5d9b355c..0e4afe76 100644
--- a/lib/metadata/mirror.c
+++ b/lib/metadata/mirror.c
@@ -136,6 +136,53 @@ uint32_t adjusted_mirror_region_size(uint32_t extent_size, uint32_t extents,
}
/*
+ * shift_mirror_images
+ * @mirrored_seg
+ * @mimage: The position (index) of the image to move to the end
+ *
+ * When dealing with removal of legs, we often move a 'removable leg'
+ * to the back of the 'areas' array. It is critically important not
+ * to simply swap it for the last area in the array. This would have
+ * the affect of reordering the remaining legs - altering position of
+ * the primary. So, we must shuffle all of the areas in the array
+ * to maintain their relative position before moving the 'removable
+ * leg' to the end.
+ *
+ * Short illustration of the problem:
+ * - Mirror consists of legs A, B, C and we want to remove A
+ * - We swap A and C and then remove A, leaving C, B
+ * This scenario is problematic in failure cases where A dies, because
+ * B becomes the primary. If the above happens, we effectively throw
+ * away any changes made between the time of failure and the time of
+ * restructuring the mirror.
+ *
+ * So, any time we want to move areas to the end to be removed, use
+ * this function.
+ */
+int shift_mirror_images(struct lv_segment *mirrored_seg, unsigned mimage)
+{
+ int i;
+ struct lv_segment_area area;
+
+ if (mimage >= mirrored_seg->area_count) {
+ log_error("Invalid index (%u) of mirror image supplied "
+ "to shift_mirror_images()", mimage);
+ return 0;
+ }
+
+ area = mirrored_seg->areas[mimage];
+
+ /* Shift remaining images down to fill the hole */
+ for (i = mimage + 1; i < mirrored_seg->area_count; i++)
+ mirrored_seg->areas[i-1] = mirrored_seg->areas[i];
+
+ /* Place this one at the end */
+ mirrored_seg->areas[i-1] = area;
+
+ return 0;
+}
+
+/*
* This function writes a new header to the mirror log header to the lv
*
* Returns: 1 on success, 0 on failure
@@ -469,13 +516,12 @@ static int _remove_mirror_images(struct logical_volume *lv,
for (s = 0; s < mirrored_seg->area_count &&
old_area_count - new_area_count < num_removed; s++) {
sub_lv = seg_lv(mirrored_seg, s);
+
if (!is_temporary_mirror_layer(sub_lv) &&
_is_mirror_image_removable(sub_lv, removable_pvs)) {
- /* Swap segment to end */
+ if (!shift_mirror_images(mirrored_seg, s))
+ return_0;
new_area_count--;
- area = mirrored_seg->areas[new_area_count];
- mirrored_seg->areas[new_area_count] = mirrored_seg->areas[s];
- mirrored_seg->areas[s] = area;
}
}
if (num_removed && old_area_count == new_area_count)
diff --git a/tools/vgreduce.c b/tools/vgreduce.c
index 5e613824..f2a5e218 100644
--- a/tools/vgreduce.c
+++ b/tools/vgreduce.c
@@ -250,9 +250,8 @@ static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
lvl2->lv != seg_lv(mirrored_seg, s))
continue;
list_del(&lvl2->list);
- area = mirrored_seg->areas[mimages - 1];
- mirrored_seg->areas[mimages - 1] = mirrored_seg->areas[s];
- mirrored_seg->areas[s] = area;
+ if (!shift_mirror_images(mirrored_seg, s))
+ return_0;
mimages--; /* FIXME Assumes uniqueness */
}
}