summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/metadata/mirror.c265
-rw-r--r--man/lvconvert.8.in43
-rw-r--r--tools/args.h1
-rw-r--r--tools/commands.h9
-rw-r--r--tools/lvconvert.c60
5 files changed, 362 insertions, 16 deletions
diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c
index e261269b..bbd41b19 100644
--- a/lib/metadata/mirror.c
+++ b/lib/metadata/mirror.c
@@ -468,6 +468,243 @@ static int _is_mirror_image_removable(struct logical_volume *mimage_lv,
}
/*
+ * _move_removable_mimages_to_end
+ *
+ * We always detach mimage LVs from the end of the areas array.
+ * This function will push 'count' mimages to the end of the array
+ * based on if their PVs are removable.
+ *
+ * This is an all or nothing function. Either the user specifies
+ * enough removable PVs to satisfy count, or they don't specify
+ * any removable_pvs at all (in which case all PVs in the mirror
+ * are considered removable).
+ */
+static int _move_removable_mimages_to_end(struct logical_volume *lv,
+ uint32_t count,
+ struct dm_list *removable_pvs)
+{
+ int i, images;
+ struct logical_volume *sub_lv;
+ struct lv_segment *mirrored_seg = first_seg(lv);
+
+ if (!removable_pvs)
+ return 1;
+
+ /*
+ * When we shift an image to the end, we must start from
+ * the begining of the list again. We must visit the
+ * images up to the last one we just moved.
+ */
+ for (images = mirrored_seg->area_count; images && count; images--) {
+ for (i = 0; i < images; i++) {
+ sub_lv = seg_lv(mirrored_seg, i);
+
+ if (!is_temporary_mirror_layer(sub_lv) &&
+ _is_mirror_image_removable(sub_lv, removable_pvs)) {
+ if (!shift_mirror_images(mirrored_seg, i))
+ return_0;
+ count--;
+ break;
+ }
+ }
+
+ /* Did we shift any images? */
+ if (i == images)
+ return 0;
+ }
+
+ return !count;
+}
+
+/*
+ * Split off 'split_count' legs from a mirror
+ *
+ * Returns: 0 on error, 1 on success
+ */
+static int _split_mirror_images(struct logical_volume *lv,
+ const char *split_name,
+ uint32_t split_count,
+ struct dm_list *removable_pvs)
+{
+ uint32_t i;
+ struct logical_volume *sub_lv, *new_lv = NULL;
+ struct logical_volume *detached_log_lv = NULL;
+ struct logical_volume *lv1 = NULL;
+ struct lv_segment *mirrored_seg = first_seg(lv);
+ struct dm_list split_images;
+ struct lv_list *lvl;
+
+ if (!(lv->status & MIRRORED)) {
+ log_error("Unable to split non-mirrored LV, %s",
+ lv->name);
+ return 0;
+ }
+
+ if (!split_count) {
+ log_error("split_count is zero!");
+ return 0;
+ }
+
+ log_verbose("Detaching %d images from mirror, %s",
+ split_count, lv->name);
+
+ if (!_move_removable_mimages_to_end(lv, split_count, removable_pvs)) {
+ /*
+ * FIXME: Allow incomplete specification of removable PVs?
+ *
+ * I am forcing the user to either specify no
+ * removable PVs or all of them. Should we allow
+ * them to just specify some - making us pick the rest?
+ */
+ log_error("Insufficient removable PVs given"
+ " to satisfy request");
+ return 0;
+ }
+
+ dm_list_init(&split_images);
+ for (i = 0; i < split_count; i++) {
+ mirrored_seg->area_count--;
+ sub_lv = seg_lv(mirrored_seg, mirrored_seg->area_count);
+
+ sub_lv->status &= ~MIRROR_IMAGE;
+ lv_set_visible(sub_lv);
+ release_lv_segment_area(mirrored_seg, mirrored_seg->area_count,
+ mirrored_seg->area_len);
+
+ if (!new_lv) {
+ new_lv = sub_lv;
+ new_lv->name = dm_pool_strdup(lv->vg->cmd->mem,
+ split_name);
+ if (!new_lv->name) {
+ log_error("Unable to rename newly split LV");
+ return 0;
+ }
+ } 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(&split_images, &lvl->list);
+ }
+ }
+
+ if (!dm_list_empty(&split_images)) {
+ size_t len = strlen(new_lv->name) + 32;
+ char *layer_name, format[len];
+
+ if (!insert_layer_for_lv(lv->vg->cmd, new_lv,
+ 0, "_mimage_%d")) {
+ log_error("Failed to build new mirror, %s",
+ new_lv->name);
+ return 0;
+ }
+
+ first_seg(new_lv)->region_size = mirrored_seg->region_size;
+
+ dm_list_iterate_items(lvl, &split_images) {
+ sub_lv = lvl->lv;
+
+ dm_snprintf(format, len, "%s_mimage_%%d",
+ new_lv->name);
+
+ layer_name = dm_pool_alloc(lv->vg->cmd->mem, len);
+ if (!layer_name) {
+ log_error("Unable to allocate memory");
+ return 0;
+ }
+ if (!generate_lv_name(lv->vg, format, layer_name, len)||
+ sscanf(layer_name, format, &i) != 1) {
+ log_error("Failed to generate new image names");
+ return 0;
+ }
+ sub_lv->name = layer_name;
+ }
+
+ if (!_merge_mirror_images(new_lv, &split_images)) {
+ log_error("Failed to group split "
+ "images into new mirror");
+ return 0;
+ }
+
+ /*
+ * We don't allow splitting a mirror that is not in-sync,
+ * so we can bring the newly split mirror up without a
+ * resync. (It will be a 'core' log mirror after all.)
+ */
+ init_mirror_in_sync(1);
+ }
+
+ /* If no more mirrors, remove mirror layer */
+ if (mirrored_seg->area_count == 1) {
+ lv1 = seg_lv(mirrored_seg, 0);
+ lv1->status &= ~MIRROR_IMAGE;
+ lv_set_visible(lv1);
+ detached_log_lv = detach_mirror_log(mirrored_seg);
+ if (!remove_layer_from_lv(lv, lv1))
+ return_0;
+ lv->status &= ~MIRRORED;
+ lv->status &= ~MIRROR_NOTSYNCED;
+ }
+
+ if (!vg_write(mirrored_seg->lv->vg)) {
+ log_error("Intermediate VG metadata write failed.");
+ return 0;
+ }
+
+ if (!suspend_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+ log_error("Failed to lock %s", mirrored_seg->lv->name);
+ vg_revert(mirrored_seg->lv->vg);
+ return 0;
+ }
+
+ if (!vg_commit(mirrored_seg->lv->vg)) {
+ resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv);
+ return 0;
+ }
+
+ log_very_verbose("Updating \"%s\" in kernel", mirrored_seg->lv->name);
+
+ /*
+ * If we have split off a mirror instead of linear (i.e. the
+ * split_images list is not empty), then we must perform a
+ * resume to get the mirror started.
+ */
+ if (!dm_list_empty(&split_images) && !resume_lv(lv->vg->cmd, new_lv)) {
+ log_error("Failed to resume newly split LV, %s", new_lv->name);
+ return 0;
+ }
+
+ /*
+ * Avoid having same mirror target loaded twice simultaneously by first
+ * resuming the removed LV which now contains an error segment.
+ * As it's now detached from mirrored_seg->lv we must resume it
+ * explicitly.
+ */
+ if (lv1 && !resume_lv(lv1->vg->cmd, lv1)) {
+ log_error("Problem resuming temporary LV, %s", lv1->name);
+ return 0;
+ }
+
+ if (!resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+ log_error("Problem reactivating %s", mirrored_seg->lv->name);
+ return 0;
+ }
+
+ if (lv1 && !_delete_lv(lv, lv1))
+ return_0;
+
+ if (detached_log_lv && !_delete_lv(lv, detached_log_lv))
+ return_0;
+
+ log_very_verbose("%" PRIu32 " image(s) detached from %s",
+ split_count, lv->name);
+
+ return 1;
+}
+
+/*
* Remove num_removed images from mirrored_seg
*
* Arguments:
@@ -1611,6 +1848,34 @@ int lv_add_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
return 0;
}
+int lv_split_mirror_images(struct logical_volume *lv, const char *split_name,
+ uint32_t split_count, struct dm_list *removable_pvs)
+{
+ int r;
+
+ /* 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 = _split_mirror_images(lv, split_name, split_count, removable_pvs);
+ if (!r)
+ return 0;
+
+ return 1;
+}
+
/*
* Generic interface for removing mirror and/or mirror log.
* 'mirror' is the number of mirrors to be removed.
diff --git a/man/lvconvert.8.in b/man/lvconvert.8.in
index 879064b7..3dcc8289 100644
--- a/man/lvconvert.8.in
+++ b/man/lvconvert.8.in
@@ -16,6 +16,13 @@ LogicalVolume[Path] [PhysicalVolume[Path][:PE[-PE]]...]
.br
.B lvconvert
+\-\-splitmirrors Images \-\-name 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 specified PhysicalVolumes.
.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,22 @@ process will not wait for notification from udev.
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
+The number of redundant Images of a mirror to be split off and used
+to form a new logical volume. A name must be supplied for the
+newly-split-off logical volume using the \-\-name argument.
+
+.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 +120,18 @@ Power of 2 chunk size for the snapshot logical volume between 4k and 512k.
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
diff --git a/tools/args.h b/tools/args.h
index 85f6a3cf..b41c275e 100644
--- a/tools/args.h
+++ b/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)
diff --git a/tools/commands.h b/tools/commands.h
index 032bde64..20db3739 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -112,6 +112,10 @@ xx(lvconvert,
"\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n\n"
"lvconvert "
+ "[--splitmirrors Images --name SplitLogicalVolumeName]\n"
+ "\tLogicalVolume[Path] [SplittablePhysicalVolume[Path]...]\n\n"
+
+ "lvconvert "
"[-s|--snapshot]\n"
"\t[-c|--chunksize]\n"
"\t[-d|--debug]\n"
@@ -123,8 +127,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",
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 80c32b17..fd5b3530 100644
--- a/tools/lvconvert.c
+++ b/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;
@@ -125,7 +127,48 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
if (arg_count(cmd, snapshot_ARG))
lp->snapshot = 1;
+ if (arg_count(cmd, splitmirrors_ARG) && arg_count(cmd, mirrors_ARG)) {
+ log_error("--mirrors 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("Please name the new logical volume using '--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 additional qualifying argument being added here.
+ */
lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0);
lp->mirrors_sign = arg_sign_value(cmd, mirrors_ARG, 0);
}
@@ -586,7 +629,7 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv
/* 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;
@@ -605,7 +648,7 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv
* 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;
@@ -729,10 +772,17 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv
/* 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;