[lvm-devel] LVM2 ./WHATS_NEW daemons/dmeventd/plugins/mirr ...

jbrassow at sourceware.org jbrassow at sourceware.org
Fri Mar 26 22:15:48 UTC 2010


CVSROOT:	/cvs/lvm2
Module name:	LVM2
Changes by:	jbrassow at sourceware.org	2010-03-26 22:15:44

Modified files:
	.              : WHATS_NEW 
	daemons/dmeventd/plugins/mirror: dmeventd_mirror.c 
	lib/activate   : activate.c 
	lib/metadata   : lv_alloc.h lv_manip.c mirror.c 
	man            : lvconvert.8.in lvcreate.8.in 
	test           : t-lvconvert-repair-policy.sh 
	                 t-lvconvert-repair.sh t-lvcreate-operation.sh 
	                 t-mirror-lvconvert.sh t-snapshots-of-mirrors.sh 
	tools          : commands.h lvconvert.c lvcreate.c 

Log message:
	Add ability to create mirrored logs for mirror LVs.
	
	This check-in enables the 'mirrored' log type.  It can be specified
	by using the '--mirrorlog' option as follows:
	#> lvcreate -m1 --mirrorlog mirrored -L 5G -n lv vg
	
	I've also included a couple updates to the testsuite.  These updates
	include tests for the new log type, and some fixes to some of the
	*lvconvert* tests.

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/WHATS_NEW.diff?cvsroot=lvm2&r1=1.1484&r2=1.1485
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c.diff?cvsroot=lvm2&r1=1.31&r2=1.32
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/lib/activate/activate.c.diff?cvsroot=lvm2&r1=1.167&r2=1.168
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/lib/metadata/lv_alloc.h.diff?cvsroot=lvm2&r1=1.26&r2=1.27
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/lib/metadata/lv_manip.c.diff?cvsroot=lvm2&r1=1.216&r2=1.217
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/lib/metadata/mirror.c.diff?cvsroot=lvm2&r1=1.108&r2=1.109
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/man/lvconvert.8.in.diff?cvsroot=lvm2&r1=1.14&r2=1.15
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/man/lvcreate.8.in.diff?cvsroot=lvm2&r1=1.16&r2=1.17
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/test/t-lvconvert-repair-policy.sh.diff?cvsroot=lvm2&r1=1.1&r2=1.2
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/test/t-lvconvert-repair.sh.diff?cvsroot=lvm2&r1=1.4&r2=1.5
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/test/t-lvcreate-operation.sh.diff?cvsroot=lvm2&r1=1.2&r2=1.3
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/test/t-mirror-lvconvert.sh.diff?cvsroot=lvm2&r1=1.16&r2=1.17
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/test/t-snapshots-of-mirrors.sh.diff?cvsroot=lvm2&r1=1.3&r2=1.4
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/tools/commands.h.diff?cvsroot=lvm2&r1=1.142&r2=1.143
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/tools/lvconvert.c.diff?cvsroot=lvm2&r1=1.122&r2=1.123
http://sourceware.org/cgi-bin/cvsweb.cgi/LVM2/tools/lvcreate.c.diff?cvsroot=lvm2&r1=1.216&r2=1.217

--- LVM2/WHATS_NEW	2010/03/26 15:45:36	1.1484
+++ LVM2/WHATS_NEW	2010/03/26 22:15:43	1.1485
@@ -1,5 +1,6 @@
 Version 2.02.63 -  
 ================================
+  Add ability to create mirrored logs for mirror LVs.
   Use a real socket for singlenode clvmd to fix clvmd's high cpu load.
   Fix clvmd cluster propagation of dmeventd monitoring mode.
   Allow ALLOC_ANYWHERE to split contiguous areas.
--- LVM2/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c	2010/01/22 12:48:58	1.31
+++ LVM2/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c	2010/03/26 22:15:43	1.32
@@ -148,6 +148,11 @@
 		return -ENOMEM;	/* FIXME Replace with generic error return - reason for failure has already got logged */
 	}
 
+	/* strip off the mirror component designations */
+	layer = strstr(lv, "_mlog");
+	if (layer)
+		*layer = '\0';
+
 	/* FIXME Is any sanity-checking required on %s? */
 	if (CMD_SIZE <= snprintf(cmd_str, CMD_SIZE, "lvconvert --config devices{ignore_suspended_devices=1} --repair --use-policies %s/%s", vg, lv)) {
 		/* this error should be caught above, but doesn't hurt to check again */
--- LVM2/lib/activate/activate.c	2010/03/05 14:48:34	1.167
+++ LVM2/lib/activate/activate.c	2010/03/26 22:15:43	1.168
@@ -703,6 +703,7 @@
 	int r = 1;
 	struct dm_list *tmp, *snh, *snht;
 	struct lv_segment *seg;
+	struct lv_segment *log_seg;
 	int (*monitor_fn) (struct lv_segment *s, int e);
 	uint32_t s;
 
@@ -738,6 +739,16 @@
 		return r;
 	}
 
+	/*
+	 * If the volume is mirrored and its log is also mirrored, monitor
+	 * the log volume as well.
+	 */
+	if ((seg = first_seg(lv)) != NULL && seg->log_lv != NULL &&
+	    (log_seg = first_seg(seg->log_lv)) != NULL &&
+	    seg_is_mirrored(log_seg))
+		if (!monitor_dev_for_events(cmd, seg->log_lv, monitor))
+			r = 0;
+
 	dm_list_iterate(tmp, &lv->segments) {
 		seg = dm_list_item(tmp, struct lv_segment);
 
--- LVM2/lib/metadata/lv_alloc.h	2010/03/01 20:00:21	1.26
+++ LVM2/lib/metadata/lv_alloc.h	2010/03/26 22:15:43	1.27
@@ -68,7 +68,8 @@
 		      uint32_t num_extra_areas,
 		      uint64_t status, uint32_t region_size);
 
-int lv_add_log_segment(struct alloc_handle *ah, struct logical_volume *log_lv);
+int lv_add_log_segment(struct alloc_handle *ah, uint32_t first_area,
+		       struct logical_volume *log_lv, uint64_t status);
 int lv_add_virtual_segment(struct logical_volume *lv, uint64_t status,
                            uint32_t extents, const struct segment_type *segtype);
 
--- LVM2/lib/metadata/lv_manip.c	2010/03/25 21:19:27	1.216
+++ LVM2/lib/metadata/lv_manip.c	2010/03/26 22:15:43	1.217
@@ -1635,14 +1635,32 @@
 
 /*
  * Turn an empty LV into a mirror log.
+ *
+ * FIXME: Mirrored logs are built inefficiently.
+ * A mirrored log currently uses the same layout that a mirror
+ * LV uses.  The mirror layer sits on top of AREA_LVs which form the
+ * legs, rather on AREA_PVs.  This is done to allow re-use of the
+ * various mirror functions to also handle the mirrored LV that makes
+ * up the log.
+ *
+ * If we used AREA_PVs under the mirror layer of a log, we could
+ * assemble it all at once by calling 'lv_add_segment' with the
+ * appropriate segtype (mirror/stripe), like this:
+ * 	lv_add_segment(ah, ah->area_count, ah->log_area_count,
+ *		       log_lv, segtype, 0, MIRROR_LOG, 0);
+ *
+ * For now, we use the same mechanism to build a mirrored log as we
+ * do for building a mirrored LV: 1) create initial LV, 2) add a
+ * mirror layer, and 3) add the remaining copy LVs
  */
-int lv_add_log_segment(struct alloc_handle *ah, struct logical_volume *log_lv)
+int lv_add_log_segment(struct alloc_handle *ah, uint32_t first_area,
+		       struct logical_volume *log_lv, uint64_t status)
 {
-	const char *segtype_name = ah->log_area_count > 1 ? "mirror" : "striped";
 
-	return lv_add_segment(ah, ah->area_count, ah->log_area_count, log_lv,
-			      get_segtype_from_string(log_lv->vg->cmd, segtype_name),
-			      0, MIRROR_LOG, 0);
+	return lv_add_segment(ah, ah->area_count + first_area, 1, log_lv,
+			      get_segtype_from_string(log_lv->vg->cmd,
+						      "striped"),
+			      0, status, 0);
 }
 
 static int _lv_extend_mirror(struct alloc_handle *ah,
--- LVM2/lib/metadata/mirror.c	2010/03/01 20:00:21	1.108
+++ LVM2/lib/metadata/mirror.c	2010/03/26 22:15:43	1.109
@@ -709,8 +709,8 @@
  *
  * Arguments:
  *   num_removed:   the requested (maximum) number of mirrors to be removed
- *   removable_pvs: if not NULL, only mirrors using PVs in this list
- *                  will be removed
+ *   removable_pvs: if not NULL and list not empty, only mirrors using PVs
+ *                  in this list will be removed
  *   remove_log:    if non-zero, log_lv will be removed
  *                  (even if it's 0, log_lv will be removed if there is no
  *                   mirror remaining after the removal)
@@ -737,6 +737,7 @@
 {
 	uint32_t m;
 	uint32_t s;
+	int removable_pvs_specified;
 	struct logical_volume *sub_lv;
 	struct logical_volume *detached_log_lv = NULL;
 	struct logical_volume *temp_layer_lv = NULL;
@@ -746,6 +747,9 @@
 	struct lv_list *lvl;
 	struct dm_list tmp_orphan_lvs;
 
+	removable_pvs_specified = (removable_pvs &&
+				   !dm_list_empty(removable_pvs)) ? 1 : 0;
+
 	if (removed)
 		*removed = 0;
 
@@ -755,13 +759,13 @@
 			 remove_log ? " and no log volume" : "");
 
 	if (collapse &&
-	    (removable_pvs || (old_area_count - num_removed != 1))) {
+	    (removable_pvs_specified || (old_area_count - num_removed != 1))) {
 		log_error("Incompatible parameters to _remove_mirror_images");
 		return 0;
 	}
 
 	/* Move removable_pvs to end of array */
-	if (removable_pvs) {
+	if (removable_pvs_specified) {
 		for (s = 0; s < mirrored_seg->area_count &&
 			    old_area_count - new_area_count < num_removed; s++) {
 			sub_lv = seg_lv(mirrored_seg, s);
@@ -1171,7 +1175,8 @@
 static int _create_mimage_lvs(struct alloc_handle *ah,
 			      uint32_t num_mirrors,
 			      struct logical_volume *lv,
-			      struct logical_volume **img_lvs)
+			      struct logical_volume **img_lvs,
+			      int log)
 {
 	uint32_t m;
 	char *img_name;
@@ -1199,14 +1204,23 @@
 			return 0;
 		}
 
-		if (!lv_add_segment(ah, m, 1, img_lvs[m],
-				    get_segtype_from_string(lv->vg->cmd,
-							    "striped"),
-				    0, 0, 0)) {
-			log_error("Aborting. Failed to add mirror image segment "
-				  "to %s. Remove new LV and retry.",
-				  img_lvs[m]->name);
-			return 0;
+		if (log) {
+			if (!lv_add_log_segment(ah, m + 1, img_lvs[m], 0)) {
+				log_error("Aborting. Failed to add mirror image segment "
+					  "to %s. Remove new LV and retry.",
+					  img_lvs[m]->name);
+				return 0;
+			}
+		} else {
+			if (!lv_add_segment(ah, m, 1, img_lvs[m],
+					    get_segtype_from_string(lv->vg->cmd,
+								    "striped"),
+					    0, 0, 0)) {
+				log_error("Aborting. Failed to add mirror image segment "
+					  "to %s. Remove new LV and retry.",
+					  img_lvs[m]->name);
+				return 0;
+			}
 		}
 	}
 
@@ -1541,17 +1555,57 @@
 				       alloc, lv->vg)))
 		return_NULL;
 
-	if (!lv_add_log_segment(ah, log_lv))
+	if (!lv_add_log_segment(ah, 0, log_lv, MIRROR_LOG))
 		return_NULL;
 
 	return log_lv;
 }
 
+/*
+ * Returns: 1 on success, 0 on error
+ */
+static int _form_mirror(struct cmd_context *cmd, struct alloc_handle *ah,
+			struct logical_volume *lv,
+			uint32_t mirrors, uint32_t region_size, int log)
+{
+	struct logical_volume **img_lvs;
+
+	/*
+	 * insert a mirror layer
+	 */
+	if (dm_list_size(&lv->segments) != 1 ||
+	    seg_type(first_seg(lv), 0) != AREA_LV)
+		if (!insert_layer_for_lv(cmd, lv, 0, "_mimage_%d"))
+			return 0;
+
+	/*
+	 * create mirror image LVs
+	 */
+	if (!(img_lvs = alloca(sizeof(*img_lvs) * mirrors))) {
+		log_error("img_lvs allocation failed. "
+			  "Remove new LV and retry.");
+		return 0;
+	}
+
+	if (!_create_mimage_lvs(ah, mirrors, lv, img_lvs, log))
+		return 0;
+
+	if (!lv_add_mirror_lvs(lv, img_lvs, mirrors,
+			       MIRROR_IMAGE | (lv->status & LOCKED),
+			       region_size)) {
+		log_error("Aborting. Failed to add mirror segment. "
+			  "Remove new LV and retry.");
+		return 0;
+	}
+
+	return 1;
+}
+
 static struct logical_volume *_set_up_mirror_log(struct cmd_context *cmd,
 						 struct alloc_handle *ah,
 						 struct logical_volume *lv,
 						 uint32_t log_count,
-						 uint32_t region_size __attribute((unused)),
+						 uint32_t region_size,
 						 alloc_policy_t alloc,
 						 int in_sync)
 {
@@ -1563,11 +1617,6 @@
 
 	init_mirror_in_sync(in_sync);
 
-	if (log_count != 1) {
-		log_error("log_count != 1 is not supported.");
-		return NULL;
-	}
-
 	/* Mirror log name is lv_name + suffix, determined as the following:
 	 *   1. suffix is:
 	 *        o "_mlog" for the original mirror LV.
@@ -1600,6 +1649,12 @@
 		return NULL;
 	}
 
+	if ((log_count > 1) &&
+	    !_form_mirror(cmd, ah, log_lv, log_count-1, region_size, 1)) {
+		log_error("Failed to form mirrored log.");
+		return NULL;
+	}
+
 	if (!_init_mirror_log(cmd, log_lv, in_sync, &lv->tags, 1)) {
 		log_error("Failed to initialise mirror log.");
 		return NULL;
@@ -1630,12 +1685,6 @@
 	struct lvinfo info;
 	int r = 0;
 
-	/* Unimplemented features */
-	if (log_count > 1) {
-		log_error("log_count > 1 is not supported");
-		return 0;
-	}
-
 	if (dm_list_size(&lv->segments) != 1) {
 		log_error("Multiple-segment mirror is not supported");
 		return 0;
@@ -1707,7 +1756,6 @@
 	struct alloc_handle *ah;
 	const struct segment_type *segtype;
 	struct dm_list *parallel_areas;
-	struct logical_volume **img_lvs;
 	struct logical_volume *log_lv = NULL;
 
 	if (stripes > 1) {
@@ -1747,34 +1795,9 @@
 	   So from here on, if failure occurs, the log must be explicitly
 	   removed and the updated vg metadata should be committed. */
 
-	/*
-	 * insert a mirror layer
-	 */
-	if (dm_list_size(&lv->segments) != 1 ||
-	    seg_type(first_seg(lv), 0) != AREA_LV)
-		if (!insert_layer_for_lv(cmd, lv, 0, "_mimage_%d"))
-			goto out_remove_log;
-
-	/*
-	 * create mirror image LVs
-	 */
-	if (!(img_lvs = alloca(sizeof(*img_lvs) * mirrors))) {
-		log_error("img_lvs allocation failed. "
-			  "Remove new LV and retry.");
-		goto out_remove_log;
-	}
-
-	if (!_create_mimage_lvs(ah, mirrors, lv, img_lvs))
+	if (!_form_mirror(cmd, ah, lv, mirrors, region_size, 0))
 		goto out_remove_log;
 
-	if (!lv_add_mirror_lvs(lv, img_lvs, mirrors,
-			       MIRROR_IMAGE | (lv->status & LOCKED),
-			       region_size)) {
-		log_error("Aborting. Failed to add mirror segment. "
-			  "Remove new LV and retry.");
-		goto out_remove_images;
-	}
-
 	if (log_count && !attach_mirror_log(first_seg(lv), log_lv))
 		stack;
 
--- LVM2/man/lvconvert.8.in	2010/02/05 22:44:37	1.14
+++ LVM2/man/lvconvert.8.in	2010/03/26 22:15:43	1.15
@@ -3,7 +3,7 @@
 lvconvert \- convert a logical volume from linear to mirror or snapshot
 .SH SYNOPSIS
 .B lvconvert
-\-m|\-\-mirrors Mirrors [\-\-mirrorlog {disk|core}] [\-\-corelog] [\-R|\-\-regionsize MirrorLogRegionSize]
+\-m|\-\-mirrors Mirrors [\-\-mirrorlog {disk|core|mirrored}] [\-\-corelog] [\-R|\-\-regionsize MirrorLogRegionSize]
 [\-A|\-\-alloc AllocationPolicy]
 [\-b|\-\-background] [\-f|\-\-force] [\-i|\-\-interval Seconds]
 [\-h|\-?|\-\-help]
@@ -83,6 +83,7 @@
 Core may be useful for short-lived mirrors: It means the mirror is
 regenerated by copying the data from the first device again every
 time the device is activated - perhaps, for example, after every reboot.
+Using "mirrored" will create a persistent log that is itself mirrored.
 .TP
 .I \-\-corelog
 The optional argument "--corelog" is the same as specifying "--mirrorlog core".
--- LVM2/man/lvcreate.8.in	2010/03/23 22:30:20	1.16
+++ LVM2/man/lvcreate.8.in	2010/03/26 22:15:43	1.17
@@ -13,7 +13,7 @@
 {\-l|\-\-extents LogicalExtentsNumber[%{VG|PVS|FREE}] |
  \-L|\-\-size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
 [\-M|\-\-persistent y|n] [\-\-minor minor]
-[\-m|\-\-mirrors Mirrors [\-\-nosync] [\-\-mirrorlog {disk|core}] [\-\-corelog]
+[\-m|\-\-mirrors Mirrors [\-\-nosync] [\-\-mirrorlog {disk|core|mirrored}] [\-\-corelog]
 [\-R|\-\-regionsize MirrorLogRegionSize]]
 [\-n|\-\-name LogicalVolumeName]
 [\-p|\-\-permission r|rw] [\-r|\-\-readahead ReadAheadSectors|auto|none]
@@ -113,9 +113,10 @@
 The optional argument --mirrorlog specifies the type of log to be used.
 The default is disk, which is persistent and requires
 a small amount of storage space, usually on a separate device from the
-data being mirrored. Using core means the mirror is regenerated
+data being mirrored.  Using core means the mirror is regenerated
 by copying the data from the first device again each time the
-device is activated, for example, after every reboot.
+device is activated, for example, after every reboot.  Using "mirrored"
+will create a persistent log that is itself mirrored.
 
 The optional argument --corelog is equivalent to --mirrorlog core.
 
--- LVM2/test/t-lvconvert-repair-policy.sh	2010/01/08 13:04:10	1.1
+++ LVM2/test/t-lvconvert-repair-policy.sh	2010/03/26 22:15:44	1.2
@@ -13,12 +13,15 @@
 
 prepare_vg 4
 
+# Clean-up and create a 2-way mirror, where the the
+# leg devices are always on $dev[12] and the log
+# is always on $dev3.  ($dev4 behaves as a spare)
 cleanup() {
 	vgreduce --removemissing $vg
 	for d in "$@"; do enable_dev $d; done
 	for d in "$@"; do vgextend $vg $d; done
 	lvremove -ff $vg/mirror
-	lvcreate -m 1 -L 1 -n mirror $vg
+	lvcreate -m 1 -l 2 -n mirror $vg $dev1 $dev2 $dev3:0
 }
 
 repair() {
@@ -28,34 +31,42 @@
 lvcreate -m 1 -L 1 -n mirror $vg
 lvchange -a n $vg/mirror
 
+# Fail a leg of a mirror.
+# Expected result: linear
 disable_dev $dev1
 lvchange --partial -a y $vg/mirror
 repair 'activation { mirror_image_fault_policy = "remove" }'
 lvs | grep -- -wi-a- # non-mirror
 cleanup $dev1
 
+# Fail a leg of a mirror.
+# Expected result: Mirror (leg replaced)
 disable_dev $dev1
 repair 'activation { mirror_image_fault_policy = "replace" }'
 lvs | grep -- mwi-a- # mirror
 lvs | grep mirror_mlog
 cleanup $dev1
 
+# Fail a leg of a mirror (use old name for policy specification)
+# Expected result: Mirror (leg replaced)
 disable_dev $dev1
 repair 'activation { mirror_device_fault_policy = "replace" }'
 lvs | grep -- mwi-a- # mirror
 lvs | grep mirror_mlog
 cleanup $dev1
 
+# Fail a leg of a mirror w/ no available spare
+# Expected result: linear
 disable_dev $dev2 $dev4
-# no room for repair, downconversion should happen
 repair 'activation { mirror_image_fault_policy = "replace" }'
 lvs | grep -- -wi-a-
 cleanup $dev2 $dev4
 
-disable_dev $dev2 $dev4
-# no room for new log, corelog conversion should happen
+# Fail the log device of a mirror w/ no available spare
+# Expected result: mirror w/ corelog
+disable_dev $dev3 $dev4
 repair 'activation { mirror_image_fault_policy = "replace" }'
 lvs
 lvs | grep -- mwi-a-
 lvs | not grep mirror_mlog
-cleanup $dev2 $dev4
+cleanup $dev3 $dev4
--- LVM2/test/t-lvconvert-repair.sh	2009/12/28 18:33:04	1.4
+++ LVM2/test/t-lvconvert-repair.sh	2010/03/26 22:15:44	1.5
@@ -69,5 +69,5 @@
 lvcreate -m 2 -l 1 -n mirror2 $vg $dev1 $dev2 $dev3 $dev4
 vgchange -a n $vg
 pvremove -ff -y $dev4
-echo 'y' | not lvconvert -y -i 1 --repair $vg/mirror2
+echo 'y' | lvconvert -y -i 1 --repair $vg/mirror2
 vgs
--- LVM2/test/t-lvcreate-operation.sh	2009/12/28 18:33:04	1.2
+++ LVM2/test/t-lvcreate-operation.sh	2010/03/26 22:15:44	1.3
@@ -25,9 +25,18 @@
 aux pvcreate --metadatacopies 0 $dev1
 aux vgcreate -c n $vg $devs
 
-#COMM create snapshots of LVs on --metadatacopies 0 PV (bz450651)
+# ---
+# Create snapshots of LVs on --metadatacopies 0 PV (bz450651)
 lvcreate -n$lv1 -l4 $vg $dev1
 lvcreate -n$lv2 -l4 -s $vg/$lv1
-#lvremove -f $vg/$lv2
 cleanup_lvs
 
+# ---
+# Create mirror on two devices with mirrored log using --alloc anywhere
+lvcreate -m 1 -l4 -n $lv1 --mirrorlog mirrored $vg --alloc anywhere $dev1 $dev2
+cleanup_lvs
+
+# --
+# Create mirror on one dev with mirrored log using --alloc anywhere, should fail
+lvcreate -m 1 -l4 -n $lv1 --mirrorlog mirrored $vg --alloc anywhere $dev1
+cleanup_lvs
--- LVM2/test/t-mirror-lvconvert.sh	2010/03/25 12:14:14	1.16
+++ LVM2/test/t-mirror-lvconvert.sh	2010/03/26 22:15:44	1.17
@@ -281,6 +281,38 @@
 check_and_cleanup_lvs_
 
 # ---
+# core log to mirrored log
+
+# change the log type from 'core' to 'mirrored'
+prepare_lvs_
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+check_mirror_count_ $vg/$lv1 2
+not_sh check_mirror_log_ $vg/$lv1
+lvconvert --mirrorlog mirrored -i1 $vg/$lv1 $dev3 $dev4
+check_no_tmplvs_ $vg/$lv1
+check_mirror_log_ $vg/$lv1
+mimages_are_redundant_ $vg $lv1
+
+# ---
+# mirrored log to core log
+
+# change the log type from 'mirrored' to 'core'
+lvconvert --mirrorlog core -i1 $vg/$lv1 $dev3 $dev4
+check_no_tmplvs_ $vg/$lv1
+not_sh check_mirror_log_ $vg/$lv1
+mimages_are_redundant_ $vg $lv1
+check_and_cleanup_lvs_
+
+# ---
+# Linear to mirror with mirrored log using --alloc anywhere
+prepare_lvs_
+lvcreate -l2 -n $lv1 $vg $dev1
+lvconvert -m +1 --mirrorlog mirrored $vg/$lv1 $dev1 $dev2 --alloc anywhere
+mimages_are_redundant_ $vg $lv1
+check_and_cleanup_lvs_
+
+
+# ---
 # check polldaemon restarts
 
 # convert inactive mirror and start polling
--- LVM2/test/t-snapshots-of-mirrors.sh	2010/01/12 14:19:46	1.3
+++ LVM2/test/t-snapshots-of-mirrors.sh	2010/03/26 22:15:44	1.4
@@ -21,11 +21,11 @@
 # Log conversion (disk -> core)
 lvconvert --mirrorlog core $vg/lv
 
-# Log conversion (core -> redundant)
-not lvconvert --mirrorlog redundant $vg/lv
+# Log conversion (core -> mirrored)
+lvconvert --mirrorlog mirrored $vg/lv
 
-# Log conversion (redundant -> core)
-# lvconvert --mirrorlog core $vg/lv
+# Log conversion (mirrored -> core)
+lvconvert --mirrorlog core $vg/lv
 
 # Log conversion (core -> disk)
 lvconvert --mirrorlog disk $vg/lv
--- LVM2/tools/commands.h	2010/03/23 22:30:20	1.142
+++ LVM2/tools/commands.h	2010/03/26 22:15:44	1.143
@@ -96,7 +96,7 @@
    "Change logical volume layout",
    0,
    "lvconvert "
-   "[-m|--mirrors Mirrors [{--mirrorlog {disk|core}|--corelog}]]\n"
+   "[-m|--mirrors Mirrors [{--mirrorlog {disk|core|mirrored}|--corelog}]]\n"
    "\t[--repair [--use-policies]]\n"
    "\t[-R|--regionsize MirrorLogRegionSize]\n"
    "\t[--alloc AllocationPolicy]\n"
@@ -156,7 +156,7 @@
    "\t{-l|--extents LogicalExtentsNumber[%{VG|PVS|FREE}] |\n"
    "\t -L|--size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
    "\t[-M|--persistent {y|n}] [--major major] [--minor minor]\n"
-   "\t[-m|--mirrors Mirrors [--nosync] [{--mirrorlog {disk|core}|--corelog}]]\n"
+   "\t[-m|--mirrors Mirrors [--nosync] [{--mirrorlog {disk|core|mirrored}|--corelog}]]\n"
    "\t[-n|--name LogicalVolumeName]\n"
    "\t[--noudevsync]\n"
    "\t[-p|--permission {r|rw}]\n"
--- LVM2/tools/lvconvert.c	2010/03/16 14:37:39	1.122
+++ LVM2/tools/lvconvert.c	2010/03/26 22:15:44	1.123
@@ -647,31 +647,72 @@
 	}
 }
 
-static int _using_corelog(struct logical_volume *lv)
+/*
+ * _get_log_count
+ * @lv: the mirror LV
+ *
+ * Get the number of on-disk copies of the log.
+ *  0  = 'core'
+ *  1  = 'disk'
+ *  2+ = 'mirrored'
+ */
+static int _get_log_count(struct logical_volume *lv)
 {
-	return !first_seg(_original_lv(lv))->log_lv;
+	struct logical_volume *log_lv;
+
+	log_lv = first_seg(_original_lv(lv))->log_lv;
+	if (!log_lv)
+		return 0;
+
+	return lv_mirror_count(log_lv);
 }
 
 static int _lv_update_log_type(struct cmd_context *cmd,
 			       struct lvconvert_params *lp,
 			       struct logical_volume *lv,
+			       struct dm_list *operable_pvs,
 			       int log_count)
 {
-	struct logical_volume *original_lv = _original_lv(lv);
-	if (_using_corelog(lv) && log_count) {
+	uint32_t region_size;
+	int old_log_count;
+	struct logical_volume *original_lv;
+	struct logical_volume *log_lv;
+
+	old_log_count = _get_log_count(lv);
+	if (old_log_count == log_count)
+		return 1;
+
+	original_lv = _original_lv(lv);
+	region_size = adjusted_mirror_region_size(lv->vg->extent_size,
+						  lv->le_count,
+						  lp->region_size);
+
+	/* Add a log where there is none */
+	if (!old_log_count) {
 		if (!add_mirror_log(cmd, original_lv, log_count,
-				    adjusted_mirror_region_size(
-					lv->vg->extent_size,
-					lv->le_count,
-					lp->region_size),
-				    lp->pvh, lp->alloc))
+				    region_size, operable_pvs, lp->alloc))
 			return_0;
-	} else if (!_using_corelog(lv) && !log_count) {
-		if (!remove_mirror_log(cmd, original_lv,
-				       lp->pv_count ? lp->pvh : NULL))
+		return 1;
+	}
+
+	/* Remove an existing log completely */
+	if (!log_count) {
+		if (!remove_mirror_log(cmd, original_lv, operable_pvs))
 			return_0;
+		return 1;
 	}
-	return 1;
+
+	log_lv = first_seg(original_lv)->log_lv;
+
+	/* Adding redundancy to the log */
+	if (old_log_count < log_count) {
+		log_error("Adding log redundancy not supported yet.");
+		log_error("Try converting the log to 'core' first.");
+		return_0;
+	}
+
+	/* Reducing redundancy of the log */
+	return remove_mirror_images(log_lv, log_count, operable_pvs, 1U);
 }
 
 /*
@@ -712,138 +753,134 @@
 	}
 }
 
-static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
-			      struct lvconvert_params *lp)
+/*
+ * _lvconvert_mirrors_parse_params
+ *
+ * This function performs the following:
+ *  1) Gets the old values of mimage and log counts
+ *  2) Parses the CLI args to find the new desired values
+ *  3) Adjusts 'lp->mirrors' to the appropriate absolute value.
+ *     (Remember, 'lp->mirrors' is specified in terms of the number of "copies"
+ *      vs. the number of mimages.  It can also be a relative value.)
+ *  4) Sets 'lp->need_polling' if collapsing
+ *  5) Validates other mirror params
+ *
+ * Returns: 1 on success, 0 on error
+ */
+static int _lvconvert_mirrors_parse_params(struct cmd_context *cmd,
+					   struct logical_volume *lv,
+					   struct lvconvert_params *lp,
+					   uint32_t *old_mimage_count,
+					   uint32_t *old_log_count,
+					   uint32_t *new_mimage_count,
+					   uint32_t *new_log_count)
 {
-	struct lv_segment *seg;
-	uint32_t existing_mirrors;
-	const char *mirrorlog;
-	unsigned log_count = 1;
-	int r = 0;
-	struct logical_volume *log_lv, *layer_lv;
-	int failed_mirrors = 0, failed_log = 0;
-	struct dm_list *old_pvh = NULL, *remove_pvs = NULL, *failed_pvs = NULL;
-
 	int repair = arg_count(cmd, repair_ARG);
-	int replace_log = 1, replace_mirrors = 1;
-	int failure_code = 0;
-
-	seg = first_seg(lv);
-	existing_mirrors = lv_mirror_count(lv);
+	const char *mirrorlog;
+	*old_mimage_count = lv_mirror_count(lv);
+	*old_log_count = _get_log_count(lv);
 
-	/* If called with no argument, try collapsing the resync layers */
+	/*
+	 * Collapsing a stack of mirrors:
+	 *
+	 * If called with no argument, try collapsing the resync layers
+	 */
 	if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, mirrorlog_ARG) &&
 	    !arg_count(cmd, corelog_ARG) && !arg_count(cmd, regionsize_ARG) &&
 	    !arg_count(cmd, splitmirrors_ARG) && !repair) {
+		*new_mimage_count = *old_mimage_count;
+		*new_log_count = *old_log_count;
+
 		if (find_temporary_mirror(lv) || (lv->status & CONVERTING))
 			lp->need_polling = 1;
 		return 1;
 	}
 
-	if (arg_count(cmd, mirrors_ARG) && repair) {
-		log_error("You may only use one of --mirrors and --repair.");
+	if ((arg_count(cmd, mirrors_ARG) && repair) ||
+	    (arg_count(cmd, mirrorlog_ARG) && repair) ||
+	    (arg_count(cmd, corelog_ARG) && repair)) {
+		log_error("--repair cannot be used with --mirrors, --mirrorlog,"
+			  " or --corelog");
+		return 0;
+	}
+
+	if (arg_count(cmd, mirrorlog_ARG) && arg_count(cmd, corelog_ARG)) {
+		log_error("--mirrorlog and --corelog are incompatible");
 		return 0;
 	}
 
 	/*
-	 * Adjust required number of mirrors
-	 *
-	 * We check mirrors_ARG again to see if it
-	 * was supplied.  If not, they want the mirror
-	 * count to remain the same.  They may be changing
-	 * the logging type.
+	 * Adjusting mimage count?
 	 */
 	if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, splitmirrors_ARG))
-		lp->mirrors = existing_mirrors;
+		lp->mirrors = *old_mimage_count;
 	else if (lp->mirrors_sign == SIGN_PLUS)
-		lp->mirrors = existing_mirrors + lp->mirrors;
+		lp->mirrors = *old_mimage_count + lp->mirrors;
 	else if (lp->mirrors_sign == SIGN_MINUS)
-		lp->mirrors = existing_mirrors - lp->mirrors;
+		lp->mirrors = *old_mimage_count - lp->mirrors;
 	else
 		lp->mirrors += 1;
 
+	*new_mimage_count = lp->mirrors;
+
+	/* Too many mimages? */
 	if (lp->mirrors > DEFAULT_MIRROR_MAX_IMAGES) {
 		log_error("Only up to %d images in mirror supported currently.",
 			  DEFAULT_MIRROR_MAX_IMAGES);
 		return 0;
 	}
 
-	/*
-	 * If we are converting from one type of mirror to another, and
-	 * the type of log wasn't specified, then let's keep the log type
-	 * the same.
-	 */
-	if ((existing_mirrors > 1) && (lp->mirrors > 1) &&
-	    (lp->mirrors != existing_mirrors) && !(lv->status & CONVERTING) &&
-	    !arg_count(cmd, mirrorlog_ARG) && !arg_count(cmd, corelog_ARG)) {
-		log_count = (first_seg(lv)->log_lv) ?
-			lv_mirror_count(first_seg(lv)->log_lv) : 0;
+	/* Did the user try to subtract more legs than available? */
+	if (lp->mirrors < 1) {
+		log_error("Logical volume %s only has %" PRIu32 " mirrors.",
+			  lv->name, *old_mimage_count);
+		return 0;
 	}
 
-	if (repair) {
-		cmd->handles_missing_pvs = 1;
-		cmd->partial_activation = 1;
-		lp->need_polling = 0;
-		if (!(lv->status & PARTIAL_LV)) {
-			log_error("The mirror is consistent. Nothing to repair.");
-			return 1;
-		}
-		if ((failed_mirrors = _failed_mirrors_count(lv)) < 0)
-			return_0;
-		lp->mirrors -= failed_mirrors;
-		log_error("Mirror status: %d of %d images failed.",
-			  failed_mirrors, existing_mirrors);
-		old_pvh = lp->pvh;
-		if (!(failed_pvs = _failed_pv_list(lv->vg)))
-			return_0;
-		lp->pvh = lp->failed_pvs = failed_pvs;
-		log_lv = first_seg(lv)->log_lv;
-		if (!log_lv || log_lv->status & PARTIAL_LV) {
-			failed_log = 1;
-			log_count = 0;
-		}
-	} else {
-		/*
-		 * Did the user try to subtract more legs than available?
-		 */
-		if (lp->mirrors < 1) {
-			log_error("Logical volume %s only has %" PRIu32 " mirrors.",
-				  lv->name, existing_mirrors);
-			return 0;
-		}
-
-		/*
-		 * Adjust log type
-		 */
-		if (arg_count(cmd, corelog_ARG))
-			log_count = 0;
+	/*
+	 * FIXME: It would be nice to say what we are adjusting to, but
+	 * I really don't know whether to specify the # of copies or mimages.
+	 */
+	if (*old_mimage_count != *new_mimage_count)
+		log_verbose("Adjusting mirror image count of %s", lv->name);
 
-		mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
-					  !log_count ? "core" : DEFAULT_MIRRORLOG);
+	/*
+	 * Adjust log type
+	 *
+	 * If we are converting from a mirror to another mirror or simply
+	 * changing the log type, we start by assuming they want the log
+	 * type the same and then parse the given args.  OTOH, If we are
+	 * converting from linear to mirror, then we start from the default
+	 * position that the user would like a 'disk' log.
+	 */
+	*new_log_count = (*old_mimage_count > 1) ? *old_log_count : 1;
+	if (!arg_count(cmd, corelog_ARG) && !arg_count(cmd, mirrorlog_ARG))
+		return 1;
 
-		if (strcmp("core", mirrorlog) && !log_count) {
-			log_error("--mirrorlog disk and --corelog "
-				  "are incompatible");
-			return 0;
-		}
+	if (arg_count(cmd, corelog_ARG))
+		*new_log_count = 0;
 
-		if (!strcmp("disk", mirrorlog))
-			log_count = 1;
-		else if (!strcmp("core", mirrorlog))
-			log_count = 0;
-		else {
-			log_error("Unknown mirrorlog type: %s", mirrorlog);
-			return 0;
-		}
+	mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
+				  !*new_log_count ? "core" : DEFAULT_MIRRORLOG);
 
-		log_verbose("Setting logging type to %s", mirrorlog);
+	if (!strcmp("mirrored", mirrorlog))
+		*new_log_count = 2;
+	else if (!strcmp("disk", mirrorlog))
+		*new_log_count = 1;
+	else if (!strcmp("core", mirrorlog))
+		*new_log_count = 0;
+	else {
+		log_error("Unknown mirrorlog type: %s", mirrorlog);
+		return 0;
 	}
 
+	log_verbose("Setting logging type to %s", mirrorlog);
+
 	/*
 	 * Region size must not change on existing mirrors
 	 */
 	if (arg_count(cmd, regionsize_ARG) && (lv->status & MIRRORED) &&
-	    (lp->region_size != seg->region_size)) {
+	    (lp->region_size != first_seg(lv)->region_size)) {
 		log_error("Mirror log region size cannot be changed on "
 			  "an existing mirror.");
 		return 0;
@@ -859,48 +896,54 @@
 		return 0;
 	}
 
-	if (repair)
-		_lvconvert_mirrors_repair_ask(cmd, failed_log, failed_mirrors,
-					      &replace_log, &replace_mirrors);
+	return 1;
+}
 
- restart:
-	/*
-	 * Converting from mirror to linear
-	 */
-	if ((lp->mirrors == 1)) {
-		if (!(lv->status & MIRRORED)) {
-			log_error("Logical volume %s is already not mirrored.",
-				  lv->name);
-			return 1;
-		}
+/*
+ * _lvconvert_mirrors_aux
+ *
+ * Add/remove mirror images and adjust log type.  'operable_pvs'
+ * are the set of PVs open to removal or allocation - depending
+ * on the operation being performed.
+ *
+ * If 'allocation_failures_ok' is set, and there is a failure to
+ * convert due to space, success will be returned.
+ */
+static int _lvconvert_mirrors_aux(struct cmd_context *cmd,
+				  struct logical_volume *lv,
+				  struct lvconvert_params *lp,
+				  struct dm_list *operable_pvs,
+				  uint32_t new_mimage_count,
+				  uint32_t new_log_count,
+				  int allocation_failures_ok)
+{
+	uint32_t region_size;
+	struct dm_list *tmp;
+	struct lv_segment *seg;
+	struct logical_volume *layer_lv;
+	uint32_t old_mimage_count = lv_mirror_count(lv);
+	uint32_t old_log_count = _get_log_count(lv);
+	int failure_code = (allocation_failures_ok) ? 1 : 0;
+
+	if ((lp->mirrors == 1) && !(lv->status & MIRRORED)) {
+		log_error("Logical volume %s is already not mirrored.",
+			  lv->name);
+		return 1;
 	}
 
-	/*
-	 * Downconversion.
-	 */
-	if (lp->mirrors < existing_mirrors) {
-		/* Reduce number of mirrors */
-		if (repair || lp->pv_count)
-			remove_pvs = lp->pvh;
+	region_size = adjusted_mirror_region_size(lv->vg->extent_size,
+						  lv->le_count,
+						  lp->region_size);
 
-		if (lp->keep_mimages) {
-			if (!lv_split_mirror_images(lv, lp->lv_split_name,
-						    existing_mirrors - lp->mirrors,
-						    remove_pvs))
-				return 0;
-		} else if (!lv_remove_mirrors(cmd, lv, existing_mirrors - lp->mirrors,
-					      (!log_count || lp->mirrors == 1) ? 1U : 0U,
-					      remove_pvs, 0))
-			return_0;
+	if (!operable_pvs)
+		operable_pvs = lp->pvh;
 
-		if (lp->mirrors > 1 &&
-		    !_lv_update_log_type(cmd, lp, lv, log_count))
-			return_0;
-	} else if (!(lv->status & MIRRORED)) {
-		/*
-		 * Converting from linear to mirror
-		 */
+	seg = first_seg(lv);
 
+	/*
+	 * Up-convert from linear to mirror
+	 */
+	if (!(lv->status & MIRRORED)) {
 		/* FIXME Share code with lvcreate */
 
 		/* FIXME Why is this restriction here?  Fix it! */
@@ -916,19 +959,22 @@
 		 * currently taken by the mirror? Would make more sense from
 		 * user perspective.
 		 */
-		if (!lv_add_mirrors(cmd, lv, lp->mirrors - 1, 1,
-				    adjusted_mirror_region_size(
-						lv->vg->extent_size,
-						lv->le_count,
-						lp->region_size),
-				    log_count, lp->pvh, lp->alloc,
-				    MIRROR_BY_LV)) {
+		if (!lv_add_mirrors(cmd, lv, new_mimage_count - 1, 1,
+				    region_size, new_log_count, operable_pvs,
+				    lp->alloc, MIRROR_BY_LV)) {
 			stack;
 			return failure_code;
 		}
 		if (lp->wait_completion)
 			lp->need_polling = 1;
-	} else if (lp->mirrors > existing_mirrors || failed_mirrors) {
+
+		goto out;
+	}
+
+	/*
+	 * Up-convert m-way mirror to n-way mirror
+	 */
+	if (new_mimage_count > old_mimage_count) {
 		if (lv->status & MIRROR_NOTSYNCED) {
 			log_error("Can't add mirror to out-of-sync mirrored "
 				  "LV: use lvchange --resync first.");
@@ -953,23 +999,23 @@
 		 * insertion to make the end result consistent with
 		 * linear-to-mirror conversion.
 		 */
-		if (!_lv_update_log_type(cmd, lp, lv, log_count)) {
+		if (!_lv_update_log_type(cmd, lp, lv,
+					 operable_pvs, new_log_count)) {
 			stack;
 			return failure_code;
 		}
+
 		/* Insert a temporary layer for syncing,
 		 * only if the original lv is using disk log. */
 		if (seg->log_lv && !_insert_lvconvert_layer(cmd, lv)) {
 			log_error("Failed to insert resync layer");
 			return 0;
 		}
+
 		/* FIXME: can't have multiple mlogs. force corelog. */
-		if (!lv_add_mirrors(cmd, lv, lp->mirrors - existing_mirrors, 1,
-				    adjusted_mirror_region_size(
-						lv->vg->extent_size,
-						lv->le_count,
-						lp->region_size),
-				    0U, lp->pvh, lp->alloc,
+		if (!lv_add_mirrors(cmd, lv,
+				    new_mimage_count - old_mimage_count, 1,
+				    region_size, 0U, operable_pvs, lp->alloc,
 				    MIRROR_BY_LV)) {
 			layer_lv = seg_lv(first_seg(lv), 0);
 			if (!remove_layer_from_lv(lv, layer_lv) ||
@@ -989,24 +1035,45 @@
 		if (seg->log_lv)
 			lv->status |= CONVERTING;
 		lp->need_polling = 1;
+
+		goto out_skip_log_convert;
 	}
 
-	if (lp->mirrors == existing_mirrors) {
-		if (_using_corelog(lv) != !log_count) {
-			if (!_lv_update_log_type(cmd, lp, lv, log_count)) {
-				stack;
-				return failure_code;
-			}
-		} else {
-			log_error("Logical volume %s already has %"
-				  PRIu32 " mirror(s).", lv->name,
-				  lp->mirrors - 1);
-			if (lv->status & CONVERTING)
-				lp->need_polling = 1;
-			return 1;
+	/*
+	 * Down-convert (reduce # of mimages).
+	 */
+	if (new_mimage_count < old_mimage_count) {
+		uint32_t nmc = old_mimage_count - new_mimage_count;
+		uint32_t nlc = (!new_log_count || lp->mirrors == 1) ? 1U : 0U;
+
+		/* FIXME: We did nlc used to be calculated that way? */
+
+		/* Reduce number of mirrors */
+		if (lp->keep_mimages) {
+			if (!lv_split_mirror_images(lv, lp->lv_split_name,
+						    nmc, operable_pvs))
+				return 0;
+		} else if (!lv_remove_mirrors(cmd, lv, nmc, nlc,
+					      operable_pvs, 0))
+			return_0;
+
+		goto out; /* Just in case someone puts code between */
+	}
+
+out:
+	/*
+	 * Converting the log type
+	 */
+	if (old_log_count != new_log_count) {
+		if (!_lv_update_log_type(cmd, lp, lv,
+					 operable_pvs, new_log_count)) {
+			stack;
+			return failure_code;
 		}
 	}
 
+out_skip_log_convert:
+
 	log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
 
 	if (!vg_write(lv->vg))
@@ -1031,35 +1098,170 @@
 		goto out;
 	}
 
-	if (failed_log || failed_mirrors) {
-		lp->pvh = old_pvh;
-		if (failed_log && replace_log) {
-			failed_log = 0;
-			log_count = 1;
-		}
-		if (replace_mirrors)
-			lp->mirrors += failed_mirrors;
-		failed_mirrors = 0;
-		existing_mirrors = lv_mirror_count(lv);
+	return 1;
+}
+
+/*
+ * _lvconvert_mirrors_repair
+ *
+ * This function operates in two phases.  First, all of the bad
+ * devices are removed from the mirror.  Then, if desired by the
+ * user, the devices are replaced.
+ *
+ * 'old_mimage_count' and 'old_log_count' are there so we know
+ * what to convert to after the removal of devices.
+ */
+static int _lvconvert_mirrors_repair(struct cmd_context *cmd,
+				     struct logical_volume *lv,
+				     struct lvconvert_params *lp,
+				     uint32_t old_mimage_count,
+				     uint32_t old_log_count)
+{
+	int failed_log = 0;
+	int failed_mirrors = 0;
+	int replace_log = 0;
+	int replace_mirrors = 0;
+	uint32_t new_log_count;
+	struct dm_list *failed_pvs = NULL;
+	struct logical_volume *log_lv;
+
+	cmd->handles_missing_pvs = 1;
+	cmd->partial_activation = 1;
+	lp->need_polling = 0;
+
+	if (!(lv->status & PARTIAL_LV)) {
+		log_error("%s is consistent. Nothing to repair.", lv->name);
+		return 1;
+	}
+
+	/*
+	 * Count the failed mimages - negative if 'lv' is not a mirror
+	 */
+	if ((failed_mirrors = _failed_mirrors_count(lv)) < 0)
+		return_0;
+
+	lp->mirrors = old_mimage_count - failed_mirrors;
+
+	if (lp->mirrors != old_mimage_count)
+		log_error("Mirror status: %d of %d images failed.",
+			  failed_mirrors, old_mimage_count);
+
+	/*
+	 * Count the failed log devices
+	 */
+	new_log_count = old_log_count;
+	log_lv = first_seg(lv)->log_lv;
+	if (log_lv) {
+		new_log_count = lv_mirror_count(log_lv);
+		if (log_lv->status & PARTIAL_LV) {
+			failed_log = 1;
+			if (log_lv->status & MIRRORED)
+				new_log_count -= _failed_mirrors_count(log_lv);
+			else
+				new_log_count = 0;
+		}
+	}
+	if (old_log_count != new_log_count)
+		log_error("Mirror log status: %d of %d images failed%s",
+			  old_log_count - new_log_count, old_log_count,
+			  (!new_log_count) ? " - switching to core" : "");
+
+	/*
+	 * Find out our policies
+	 */
+	_lvconvert_mirrors_repair_ask(cmd, failed_log, failed_mirrors,
+				      &replace_log, &replace_mirrors);
+
+	/*
+	 * First phase - remove faulty devices
+	 */
+	if (!(failed_pvs = _failed_pv_list(lv->vg)))
+		return_0;
+
+	/*
+	 * We must adjust the log first, or the entire mirror
+	 * will get stuck during a suspend.
+	 */
+	if (!_lv_update_log_type(cmd, lp, lv, failed_pvs, new_log_count))
+		return 0;
+
+	if (!_lvconvert_mirrors_aux(cmd, lv, lp, failed_pvs,
+				    lp->mirrors, new_log_count, 0))
+		return 0;
+
+	/*
+	 * Second phase - replace faulty devices
+	 *
+	 * FIXME: It would be nice to do this all in one step, but
+	 *        for simplicity, we replace mimages first and then
+	 *        work on the log.
+	 */
+	if (replace_mirrors && (old_mimage_count != lp->mirrors)) {
+		lp->mirrors = old_mimage_count;
+		if (!_lvconvert_mirrors_aux(cmd, lv, lp, NULL,
+					    old_mimage_count, new_log_count, 1))
+			return 0;
+	}
+
+	log_lv = first_seg(lv)->log_lv;
+	if (replace_log && (old_log_count != new_log_count)) {
 		/*
-		 * Ignore failure in upconversion if this is a policy-driven
-		 * action. If we got this far, only unexpected failures are
-		 * reported.
+		 * If we are up-converting the log from linear to
+		 * mirrored, then we must use '_lvconvert_mirrors_aux'
 		 */
-		if (arg_count(cmd, use_policies_ARG))
-			failure_code = 1;
-		/* Now replace missing devices. */
-		if (replace_log || replace_mirrors)
-			goto restart;
+		if ((new_log_count == 1) && (old_log_count > 1)) {
+			if (!_lvconvert_mirrors_aux(cmd, log_lv, lp, NULL,
+						    old_log_count, 0, 1))
+				return 0;
+		} else if (!_lv_update_log_type(cmd, lp, lv,
+						lp->pvh, new_log_count))
+			return 0;
 	}
 
+	return 1;
+}
+
+/*
+ * _lvconvert_mirrors
+ *
+ * Determine what is being done.  Are we doing a conversion, repair, or
+ * collapsing a stack?  Once determined, call helper functions.
+ */
+static int _lvconvert_mirrors(struct cmd_context *cmd,
+			      struct logical_volume *lv,
+			      struct lvconvert_params *lp)
+{
+	int repair = arg_count(cmd, repair_ARG);
+	uint32_t old_mimage_count;
+	uint32_t old_log_count;
+	uint32_t new_mimage_count;
+	uint32_t new_log_count;
+
+	/* Adjust mimage and/or log count */
+	if (!_lvconvert_mirrors_parse_params(cmd, lv, lp,
+					     &old_mimage_count, &old_log_count,
+					     &new_mimage_count, &new_log_count))
+		return 0;
+
+	/* Nothing to do?  (Probably finishing collapse.) */
+	if ((old_mimage_count == new_mimage_count) &&
+	    (old_log_count == new_log_count) && !repair)
+		return 1;
+
+	if (repair)
+		return _lvconvert_mirrors_repair(cmd, lv, lp,
+						 old_mimage_count,
+						 old_log_count);
+
+	if (!_lvconvert_mirrors_aux(cmd, lv, lp, NULL,
+				    new_mimage_count, new_log_count, 0))
+		return 0;
+
 	if (!lp->need_polling)
 		log_print("Logical volume %s converted.", lv->name);
 
-	r = 1;
-out:
 	backup(lv->vg);
-	return r;
+	return 1;
 }
 
 static int lvconvert_snapshot(struct cmd_context *cmd,
--- LVM2/tools/lvcreate.c	2010/03/23 22:30:20	1.216
+++ LVM2/tools/lvcreate.c	2010/03/26 22:15:44	1.217
@@ -337,12 +337,14 @@
 	mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
 				  corelog ? "core" : DEFAULT_MIRRORLOG);
 
-	if (!strcmp("disk", mirrorlog)) {
-		if (corelog) {
-			log_error("--mirrorlog disk and --corelog "
-				  "are incompatible");
-			return 0;
-		}
+	if (strcmp("core", mirrorlog) && corelog) {
+		log_error("Please use only one of --mirrorlog or --corelog");
+		return 0;
+	}
+
+	if (!strcmp("mirrored", mirrorlog)) {
+		lp->log_count = 2;
+	} else if (!strcmp("disk", mirrorlog)) {
 		lp->log_count = 1;
 	} else if (!strcmp("core", mirrorlog))
 		lp->log_count = 0;




More information about the lvm-devel mailing list