1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-01-03 05:18:29 +03:00

This patch adds the capability to split off a mirror legs.

It is pretty much the same as reducing the number of
mirror legs, but we just don't delete them afterwards.

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

If more than one leg is split off, a new mirror will be the
result.  The newly split off mirror will have a 'core' log.
Example:
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
  LV            Copy%  Devices
  lv            100.00 lv_mimage_0(0),lv_mimage_1(0),lv_mimage_2(0),lv_mimage_3(0)
  [lv_mimage_0]        /dev/sdb1(0)
  [lv_mimage_1]        /dev/sdc1(0)
  [lv_mimage_2]        /dev/sdd1(0)
  [lv_mimage_3]        /dev/sde1(0)
  [lv_mlog]            /dev/sdi1(0)
[root@bp-01 LVM2]# lvconvert --splitmirrors 2 --name split vg/lv /dev/sd[ce]1
  Logical volume lv converted.
[root@bp-01 LVM2]# !lvs
lvs -a -o name,copy_percent,devices
  LV               Copy%  Devices
  lv               100.00 lv_mimage_0(0),lv_mimage_2(0)
  [lv_mimage_0]           /dev/sdb1(0)
  [lv_mimage_2]           /dev/sdd1(0)
  [lv_mlog]               /dev/sdi1(0)
  split            100.00 split_mimage_0(0),split_mimage_1(0)
  [split_mimage_0]        /dev/sde1(0)
  [split_mimage_1]        /dev/sdc1(0)

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.

Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
This commit is contained in:
Jonathan Earl Brassow 2010-01-08 22:00:31 +00:00
parent c9f27b1ca1
commit 72e0743621
5 changed files with 363 additions and 17 deletions

View File

@ -467,6 +467,243 @@ static int _is_mirror_image_removable(struct logical_volume *mimage_lv,
return 1; return 1;
} }
/*
* _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 * Remove num_removed images from mirrored_seg
* *
@ -1611,6 +1848,34 @@ int lv_add_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
return 0; 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. * Generic interface for removing mirror and/or mirror log.
* 'mirror' is the number of mirrors to be removed. * 'mirror' is the number of mirrors to be removed.

View File

@ -14,6 +14,13 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot
LogicalVolume[Path] [PhysicalVolume[Path][:PE[-PE]]...] LogicalVolume[Path] [PhysicalVolume[Path][:PE[-PE]]...]
.br .br
.br
.B lvconvert
\-\-splitmirrors Images \-\-name SplitLogicalVolumeName
.br
MirrorLogicalVolume[Path] [SplittablePhysicalVolume[Path][:PE[-PE]]...]
.br
.br .br
.B lvconvert .B lvconvert
\-s|\-\-snapshot [\-c|\-\-chunksize ChunkSize] \-s|\-\-snapshot [\-c|\-\-chunksize ChunkSize]
@ -47,7 +54,7 @@ the freed extents come first from the specified PhysicalVolumes.
.SH OPTIONS .SH OPTIONS
See \fBlvm\fP for common options. See \fBlvm\fP for common options.
.br .br
Exactly one of \-\-mirrors, \-\-repair or \-\-snapshot arguments required. Exactly one of \-\-splitmirrors, \-\-mirrors, \-\-repair or \-\-snapshot arguments required.
.br .br
.TP .TP
.I \-m, \-\-mirrors Mirrors .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 It will continue irrespective of any possible udev processing
in the background. You should only use this if udev is not running in the background. You should only use this if udev is not running
or has rules that ignore the devices LVM2 creates. or has rules that ignore the devices LVM2 creates.
.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 .br
.TP
.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 .TP
.I \-s, \-\-snapshot .I \-s, \-\-snapshot
Create a snapshot from existing logical volume using another 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. Controls zeroing of the first KB of data in the snapshot.
If the volume is read-only the snapshot will not be zeroed. If the volume is read-only the snapshot will not be zeroed.
.br .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 .SH Examples
"lvconvert -m1 vg00/lvol1" "lvconvert -m1 vg00/lvol1"
.br .br

View File

@ -50,6 +50,7 @@ arg(nosync_ARG, '\0', "nosync", NULL, 0)
arg(resync_ARG, '\0', "resync", NULL, 0) arg(resync_ARG, '\0', "resync", NULL, 0)
arg(corelog_ARG, '\0', "corelog", NULL, 0) arg(corelog_ARG, '\0', "corelog", NULL, 0)
arg(mirrorlog_ARG, '\0', "mirrorlog", string_arg, 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(repair_ARG, '\0', "repair", NULL, 0)
arg(use_policies_ARG, '\0', "use-policies", NULL, 0) arg(use_policies_ARG, '\0', "use-policies", NULL, 0)
arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0) arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0)

View File

@ -111,6 +111,10 @@ xx(lvconvert,
"\t[--version]" "\n" "\t[--version]" "\n"
"\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n\n" "\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n\n"
"lvconvert "
"[--splitmirrors Images --name SplitLogicalVolumeName]\n"
"\tLogicalVolume[Path] [SplittablePhysicalVolume[Path]...]\n\n"
"lvconvert " "lvconvert "
"[-s|--snapshot]\n" "[-s|--snapshot]\n"
"\t[-c|--chunksize]\n" "\t[-c|--chunksize]\n"
@ -123,8 +127,9 @@ xx(lvconvert,
"\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n", "\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n",
alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG, alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG,
mirrorlog_ARG, mirrors_ARG, noudevsync_ARG, regionsize_ARG, repair_ARG, splitmirrors_ARG, name_ARG, mirrorlog_ARG, mirrors_ARG, noudevsync_ARG,
snapshot_ARG, test_ARG, use_policies_ARG, yes_ARG, force_ARG, zero_ARG) regionsize_ARG, repair_ARG, snapshot_ARG, test_ARG, use_policies_ARG,
yes_ARG, force_ARG, zero_ARG)
xx(lvcreate, xx(lvcreate,
"Create a logical volume", "Create a logical volume",

View File

@ -22,6 +22,7 @@ struct lvconvert_params {
const char *origin; const char *origin;
const char *lv_name; const char *lv_name;
const char *lv_split_name;
const char *lv_name_full; const char *lv_name_full;
const char *vg_name; const char *vg_name;
int wait_completion; int wait_completion;
@ -32,6 +33,7 @@ struct lvconvert_params {
uint32_t mirrors; uint32_t mirrors;
sign_t mirrors_sign; sign_t mirrors_sign;
uint32_t keep_mimages;
struct segment_type *segtype; 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)) if (arg_count(cmd, snapshot_ARG))
lp->snapshot = 1; 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)) { 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 = arg_uint_value(cmd, mirrors_ARG, 0);
lp->mirrors_sign = arg_sign_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 called with no argument, try collapsing the resync layers */
if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, mirrorlog_ARG) && if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, mirrorlog_ARG) &&
!arg_count(cmd, corelog_ARG) && !arg_count(cmd, regionsize_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)) if (find_temporary_mirror(lv) || (lv->status & CONVERTING))
lp->need_polling = 1; lp->need_polling = 1;
return 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 * count to remain the same. They may be changing
* the logging type. * the logging type.
*/ */
if (!arg_count(cmd, mirrors_ARG)) if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, splitmirrors_ARG))
lp->mirrors = existing_mirrors; lp->mirrors = existing_mirrors;
else if (lp->mirrors_sign == SIGN_PLUS) else if (lp->mirrors_sign == SIGN_PLUS)
lp->mirrors = existing_mirrors + lp->mirrors; 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 */ /* Reduce number of mirrors */
if (repair || lp->pv_count) if (repair || lp->pv_count)
remove_pvs = lp->pvh; remove_pvs = lp->pvh;
if (!lv_remove_mirrors(cmd, lv, existing_mirrors - lp->mirrors,
(corelog || lp->mirrors == 1) ? 1U : 0U, if (lp->keep_mimages) {
remove_pvs, 0)) 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; return_0;
if (lp->mirrors > 1 && if (lp->mirrors > 1 &&
!_lv_update_log_type(cmd, lp, lv, corelog)) !_lv_update_log_type(cmd, lp, lv, corelog))
return_0; return_0;