[lvm-devel] [PATCH] LVM: add split capability - a more correct approach

Jonathan Brassow jbrassow at redhat.com
Wed Nov 11 23:12:06 UTC 2009


Changes from previous patch are mentioned in the patch header...

 brassow

This patch adds the capability to split off a mirror leg.
It is pretty much the same as reducing the number of
mirror legs, but we just don't delete it afterwards.

The following command line interface is enforced:
  prompt> lvconvert --splitmirror <n> -n <name> <VG>/<LV>
where 'n' is the number of legs to split off, and
where 'name' is the name of the newly split off logical volume.

It can be seen that '--splitmirror <n>' is exactly the same
as '--mirrors -<n>' (note the minus sign), except there is the
additional notion to keep the image being detached from the
mirror instead of just throwing it away.  So, we _could_ have
a '--keep' argument or something that could be used in
conjunction with '--mirrors' to denote the fact that we wish
to keep the image being detached.  It has been suggested that
we could have two ways of specifying this action.  I have
not implemented that here.  I am strictly choosing '--splitmirror'
and purposefully choosing not to have something additional
for '--mirrors'.  I think this prevents confusion.  The
additional argument for '--mirrors' will have to come by way
of another patch.

Also, I have chosen to make the '-n <name>' argument manditory.
Perhaps in the future when we decide on a useful default name
we can relax this enforcement.  There is no reason to withhold
the capability because we haven't formed a concensous on what
a default name should be.  [Perhaps we will make a better
informed decision on that when we begin to implement
'--track_deltas'.]

[This patch differs from the previous version in that both
'--splitmirrors <n>' and '--mirrors -<n>' use the function
release_lv_segment_area instead of '--splitmirrors' using
the alternate function remove_seg_from_segs_using_this_lv.
Both patches will produce correct results, but this patch
is more correct, and thus more "future proof".]

Signed-off-by: Jonathan Brassow <jbrassow at redhat.com>

Index: LVM2/lib/metadata/mirror.c
===================================================================
--- LVM2.orig/lib/metadata/mirror.c
+++ LVM2/lib/metadata/mirror.c
@@ -472,6 +472,9 @@ static int _is_mirror_image_removable(st
  *                  mirror layer and merge mirrors to the original LV.
  *                  removable_pvs should be NULL and num_removed should be
  *                  seg->area_count - 1.
+ *   split:	    if non-NULL, split the mimage off rather than removing
+ *		    it, making this argument its name (only one leg can be
+ *                  split off at a time).
  *   removed:       if non NULL, the number of removed mirror images is set
  *                  as a result
  *
@@ -487,6 +490,7 @@ static int _remove_mirror_images(struct 
 				 uint32_t num_removed,
 				 struct dm_list *removable_pvs,
 				 unsigned remove_log, unsigned collapse,
+				 const char *split,
 				 uint32_t *removed)
 {
 	uint32_t m;
@@ -535,15 +539,30 @@ static int _remove_mirror_images(struct 
 	/* Remove mimage LVs from the segment */
 	dm_list_init(&tmp_orphan_lvs);
 	for (m = new_area_count; m < mirrored_seg->area_count; m++) {
-		seg_lv(mirrored_seg, m)->status &= ~MIRROR_IMAGE;
-		lv_set_visible(seg_lv(mirrored_seg, m));
-		if (!(lvl = dm_pool_alloc(lv->vg->cmd->mem, sizeof(*lvl)))) {
-			log_error("lv_list alloc failed");
-			return 0;
+		sub_lv = seg_lv(mirrored_seg, m);
+
+		sub_lv->status &= ~MIRROR_IMAGE;
+		lv_set_visible(sub_lv);
+		release_lv_segment_area(mirrored_seg, m,
+					mirrored_seg->area_len);
+		if (split) {
+			sub_lv->name = dm_pool_strdup(lv->vg->cmd->mem, split);
+			if (!sub_lv->name) {
+				log_error("Unable to rename newly split LV");
+				return 0;
+			}
+
+			break;
+		} else {
+			lvl = dm_pool_alloc(lv->vg->cmd->mem, sizeof(*lvl));
+			if (!lvl) {
+				log_error("lv_list alloc failed");
+				return 0;
+			}
+
+			lvl->lv = sub_lv;
+			dm_list_add(&tmp_orphan_lvs, &lvl->list);
 		}
-		lvl->lv = seg_lv(mirrored_seg, m);
-		dm_list_add(&tmp_orphan_lvs, &lvl->list);
-		release_lv_segment_area(mirrored_seg, m, mirrored_seg->area_len);
 	}
 	mirrored_seg->area_count = new_area_count;
 
@@ -671,7 +690,8 @@ int remove_mirror_images(struct logical_
 			removed_once = first_seg(next_lv)->area_count - 1;
 
 		if (!_remove_mirror_images(next_lv, removed_once,
-					   removable_pvs, remove_log, 0, &r))
+					   removable_pvs, remove_log,
+					   0, NULL, &r))
 			return_0;
 
 		if (r < removed_once) {
@@ -745,7 +765,7 @@ int collapse_mirrored_lv(struct logical_
 
 		if (!_remove_mirror_images(mirror_seg->lv,
 					   mirror_seg->area_count - 1,
-					   NULL, 1, 1, NULL)) {
+					   NULL, 1, 1, NULL, NULL)) {
 			log_error("Failed to release mirror images");
 			return 0;
 		}
@@ -866,7 +886,7 @@ int reconfigure_mirror_images(struct lv_
 	init_mirror_in_sync(in_sync);
 
 	r = _remove_mirror_images(mirrored_seg->lv, old_num_mirrors - num_mirrors,
-				  removable_pvs, remove_log, 0, NULL);
+				  removable_pvs, remove_log, 0, NULL, NULL);
 	if (!r)
 		/* Unable to remove bad devices */
 		return 0;
@@ -1586,6 +1606,49 @@ int lv_add_mirrors(struct cmd_context *c
 	return 0;
 }
 
+int lv_split_mirror_images(struct logical_volume *lv, const char *split_lv_name,
+			   uint32_t split_count, struct dm_list *removable_pvs)
+{
+	int r;
+
+	/*
+	 * FIXME: Need ability to split off more than one mirror leg
+	 *
+	 * Right now, we only allow the user to split a single
+	 * leg off at a time.  In the future, we might allow a
+	 * 4-way mirror to be split into 2 2-way mirrors, but
+	 * not right now.
+	 */
+	if (split_count != 1) {
+		log_error("Unable to split more than one leg from a mirror.");
+		return_0;
+	}
+
+	/* Can't split a mirror that is not in-sync... unless force? */
+	if (!_mirrored_lv_in_sync(lv)) {
+		log_error("Unable to split mirror that is not in-sync.");
+		return_0;
+	}
+
+	/*
+	 * FIXME: Generate default name when not supplied.
+	 *
+	 * If we were going to generate a default name, we would
+	 * do it here.  Better to wait for a decision on the form
+	 * of the default name when '--track_deltas' (the ability
+	 * to merge a split leg back in and only copy the changes)
+	 * is being implemented.  For now, we force the user to
+	 * come up with a name for their LV.
+	 */
+	r = _remove_mirror_images(lv, split_count,
+				  removable_pvs, 0, 0,
+				  split_lv_name, NULL);
+	if (!r)
+		return 0;
+
+	return 1;
+}
+
 /*
  * Generic interface for removing mirror and/or mirror log.
  * 'mirror' is the number of mirrors to be removed.
Index: LVM2/man/lvconvert.8.in
===================================================================
--- LVM2.orig/man/lvconvert.8.in
+++ LVM2/man/lvconvert.8.in
@@ -16,6 +16,13 @@ LogicalVolume[Path] [PhysicalVolume[Path
 
 .br
 .B lvconvert
+\-\-splitmirrors Images \-n SplitLogicalVolumeName
+.br
+MirrorLogicalVolume[Path] [SplittablePhysicalVolume[Path][:PE[-PE]]...]
+.br
+
+.br
+.B lvconvert
 \-s|\-\-snapshot [\-c|\-\-chunksize ChunkSize]
 [\-h|\-?|\-\-help]
 [\-\-noudevsync]
@@ -47,7 +54,7 @@ the freed extents come first from the sp
 .SH OPTIONS
 See \fBlvm\fP for common options.
 .br
-Exactly one of \-\-mirrors, \-\-repair or \-\-snapshot arguments required.
+Exactly one of \-\-splitmirrors, \-\-mirrors, \-\-repair or \-\-snapshot arguments required.
 .br
 .TP
 .I \-m, \-\-mirrors Mirrors
@@ -85,16 +92,23 @@ process will not wait for notification f
 It will continue irrespective of any possible udev processing
 in the background.  You should only use this if udev is not running
 or has rules that ignore the devices LVM2 creates.
+.br
+
+
 .TP
-.I \-\-repair
-Repair a mirror after suffering a disk failure. The mirror will be brought back
-into a consistent state.  By default, the original number of mirrors will be
-restored if possible.  Specify \-y on the command line to skip the prompts.
-Use \-f if you do not want any replacement.  Additionally, you may use
-\-\-use-policies to use the device replacement policy specified in lvm.conf,
-viz. activation/mirror_log_fault_policy or
-activation/mirror_device_fault_policy.
+.I \-\-splitmirrors Images
+Specifies how many Images, or legs, of a mirror you wish to split
+off and form a new logical volume from.  A name must be supplied
+for the newly-split-off logical volume using the \-\-name argument.
+Only one image can be split off at this time (i.e. \-\-splitmirrors 1).
+
+.TP
+.I \-n Name
+The name to apply to a logical volume which has been split off from
+a mirror logical volume.
 .br
+
+
 .TP
 .I \-s, \-\-snapshot
 Create a snapshot from existing logical volume using another
@@ -107,6 +121,18 @@ Power of 2 chunk size for the snapshot l
 Controls zeroing of the first KB of data in the snapshot.
 If the volume is read-only the snapshot will not be zeroed.
 .br
+
+
+.TP
+.I \-\-repair
+Repair a mirror after suffering a disk failure. The mirror will be brought back
+into a consistent state.  By default, the original number of mirrors will be
+restored if possible.  Specify \-y on the command line to skip the prompts.
+Use \-f if you do not want any replacement.  Additionally, you may use
+\-\-use-policies to use the device replacement policy specified in lvm.conf,
+viz. activation/mirror_log_fault_policy or
+activation/mirror_device_fault_policy.
+.br
 .SH Examples
 "lvconvert -m1 vg00/lvol1"
 .br
Index: LVM2/tools/args.h
===================================================================
--- LVM2.orig/tools/args.h
+++ LVM2/tools/args.h
@@ -50,6 +50,7 @@ arg(nosync_ARG, '\0', "nosync", NULL, 0)
 arg(resync_ARG, '\0', "resync", NULL, 0)
 arg(corelog_ARG, '\0', "corelog", NULL, 0)
 arg(mirrorlog_ARG, '\0', "mirrorlog", string_arg, 0)
+arg(splitmirrors_ARG, '\0', "splitmirrors", int_arg, 0)
 arg(repair_ARG, '\0', "repair", NULL, 0)
 arg(use_policies_ARG, '\0', "use-policies", NULL, 0)
 arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0)
Index: LVM2/tools/commands.h
===================================================================
--- LVM2.orig/tools/commands.h
+++ LVM2/tools/commands.h
@@ -96,6 +96,7 @@ xx(lvconvert,
    0,
    "lvconvert "
    "[-m|--mirrors Mirrors [{--mirrorlog {disk|core}|--corelog}]]\n"
+   "[--splitmirrors Images -n SplitLogicalVolumeName]\n"
    "\t[--repair [--use-policies]]\n"
    "\t[-R|--regionsize MirrorLogRegionSize]\n"
    "\t[--alloc AllocationPolicy]\n"
@@ -122,8 +123,9 @@ xx(lvconvert,
    "\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n",
 
    alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG,
-   mirrorlog_ARG, mirrors_ARG, noudevsync_ARG, regionsize_ARG, repair_ARG,
-   snapshot_ARG, test_ARG, use_policies_ARG, yes_ARG, force_ARG, zero_ARG)
+   splitmirrors_ARG, name_ARG, mirrorlog_ARG, mirrors_ARG, noudevsync_ARG,
+   regionsize_ARG, repair_ARG, snapshot_ARG, test_ARG, use_policies_ARG,
+   yes_ARG, force_ARG, zero_ARG)
 
 xx(lvcreate,
    "Create a logical volume",
Index: LVM2/tools/lvconvert.c
===================================================================
--- LVM2.orig/tools/lvconvert.c
+++ LVM2/tools/lvconvert.c
@@ -22,6 +22,7 @@ struct lvconvert_params {
 
 	const char *origin;
 	const char *lv_name;
+	const char *lv_split_name;
 	const char *lv_name_full;
 	const char *vg_name;
 	int wait_completion;
@@ -32,6 +33,7 @@ struct lvconvert_params {
 
 	uint32_t mirrors;
 	sign_t mirrors_sign;
+	uint32_t keep_mimages;
 
 	struct segment_type *segtype;
 
@@ -124,7 +126,48 @@ static int _read_params(struct lvconvert
 	if (arg_count(cmd, snapshot_ARG))
 		lp->snapshot = 1;
 
+	if (arg_count(cmd, splitmirrors_ARG) && arg_count(cmd, mirrors_ARG)) {
+		log_error("--mirrors (-m) and --splitmirrors are "
+			  "mutually exclusive");
+		return 0;
+	}
+
+	/*
+	 * The '--splitmirrors n' argument is equivalent to '--mirrors -n'
+	 * (note the minus sign), except that it signifies the additional
+	 * intent to keep the mimage that is detached, rather than
+	 * discarding it.
+	 */
+	if (arg_count(cmd, splitmirrors_ARG)) {
+		if (!arg_count(cmd, name_ARG)) {
+			log_error("The split off mirror image requires a name");
+			return 0;
+		}
+
+		lp->lv_split_name = arg_value(cmd, name_ARG);
+		if (!apply_lvname_restrictions(lp->lv_split_name))
+			return_0;
+
+		lp->keep_mimages = 1;
+		if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
+			log_error("Argument to --splitmirrors"
+				  " cannot be negative");
+			return 0;
+		}
+		lp->mirrors = arg_uint_value(cmd, splitmirrors_ARG, 0);
+		lp->mirrors_sign = SIGN_MINUS;
+	} else if (arg_count(cmd, name_ARG)) {
+		log_error("The 'name' argument is only valid"
+			  " with --splitmirrors");
+		return 0;
+	}
+
 	if (arg_count(cmd, mirrors_ARG)) {
+		/*
+		 * --splitmirrors has been chosen as the mechanism for
+		 * specifying the intent of detaching and keeping a mimage
+		 * versus an argument such as '--keep' being added here.
+		 */
 		lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0);
 		lp->mirrors_sign = arg_sign_value(cmd, mirrors_ARG, 0);
 	}
@@ -543,7 +586,7 @@ static int _lvconvert_mirrors(struct cmd
 	/* 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) &&
-	    !repair) {
+	    !arg_count(cmd, splitmirrors_ARG) && !repair) {
 		if (find_temporary_mirror(lv) || (lv->status & CONVERTING))
 			lp->need_polling = 1;
 		return 1;
@@ -562,7 +605,7 @@ static int _lvconvert_mirrors(struct cmd
 	 * count to remain the same.  They may be changing
 	 * the logging type.
 	 */
-	if (!arg_count(cmd, mirrors_ARG))
+	if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, splitmirrors_ARG))
 		lp->mirrors = existing_mirrors;
 	else if (lp->mirrors_sign == SIGN_PLUS)
 		lp->mirrors = existing_mirrors + lp->mirrors;
@@ -668,10 +711,17 @@ static int _lvconvert_mirrors(struct cmd
 		/* Reduce number of mirrors */
 		if (repair || lp->pv_count)
 			remove_pvs = lp->pvh;
-		if (!lv_remove_mirrors(cmd, lv, existing_mirrors - lp->mirrors,
-				       (corelog || lp->mirrors == 1) ? 1U : 0U,
-				       remove_pvs, 0))
+
+		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,
+					      (corelog || lp->mirrors == 1) ? 1U : 0U,
+					      remove_pvs, 0))
 			return_0;
+
 		if (lp->mirrors > 1 &&
 		    !_lv_update_log_type(cmd, lp, lv, corelog))
 			return_0;





More information about the lvm-devel mailing list