1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

Implement, test and document (first iteration of) lvconvert --repair.

This commit is contained in:
Petr Rockai 2009-04-23 16:56:21 +00:00
parent 54120cb254
commit eb23252b61
6 changed files with 243 additions and 86 deletions

View File

@ -1,5 +1,6 @@
Version 2.02.46 -
================================
Implement lvconvert --repair, for repairing partially failed mirrors.
Fix vgreduce --removemissing failure exit code.
Fix remote metadata backup for clvmd.
Alloc PV internal structure from VG mempool if possible.

View File

@ -29,7 +29,7 @@ It is also used to add and remove disk logs from mirror devices.
.SH OPTIONS
See \fBlvm\fP for common options.
.br
Exactly one of \-\-mirrors or \-\-snapshot arguments required.
Exactly one of \-\-mirrors, \-\-repair or \-\-snapshot arguments required.
.br
.TP
.I \-m, \-\-mirrors Mirrors
@ -61,6 +61,12 @@ Run the daemon in the background.
Report progress as a percentage at regular intervals.
.br
.TP
.I \-\-repair
Repair a mirror that has suffered a disk failure. The mirror will be brought
back into a consistent state, and if possible, the original number of
mirrors will be restored.
.br
.TP
.I \-s, \-\-snapshot
Create a snapshot from existing logical volume using another
existing logical volume as its origin.

View File

@ -0,0 +1,46 @@
#!/bin/bash
# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License v.2.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
. ./test-utils.sh
prepare_vg 4
vgreduce $vg $dev4
lvcreate -m 1 -L 1 -n mirror $vg
lvchange -a n $vg/mirror
vgextend $vg $dev4
disable_dev $dev1
lvchange --partial -a y $vg/mirror
not vgreduce -v --removemissing $vg
lvconvert -i 1 --repair $vg/mirror
vgreduce --removemissing $vg
enable_dev $dev1
vgextend $vg $dev1
disable_dev $dev2
lvconvert -i 1 --repair $vg/mirror
vgreduce --removemissing $vg
enable_dev $dev2
vgextend $vg $dev2
disable_dev $dev3
lvconvert -i 1 --repair $vg/mirror
vgreduce --removemissing $vg
enable_dev $dev3
vgextend $vg $dev3
lvcreate -m 2 -l 1 -n mirror2 $vg $dev1 $dev2 $dev3 $dev4
vgchange -a n $vg
pvremove -ff -y $dev4
echo 'y' | not lvconvert -i 1 --repair $vg/mirror2
vgs

View File

@ -49,6 +49,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(repair_ARG, '\0', "repair", NULL, 0)
arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0)
arg(config_ARG, '\0', "config", string_arg, 0)
arg(trustcache_ARG, '\0', "trustcache", NULL, 0)

View File

@ -94,6 +94,7 @@ xx(lvconvert,
0,
"lvconvert "
"[-m|--mirrors Mirrors [{--mirrorlog {disk|core}|--corelog}]]\n"
"\t[--repair]\n"
"\t[-R|--regionsize MirrorLogRegionSize]\n"
"\t[--alloc AllocationPolicy]\n"
"\t[-b|--background]\n"
@ -115,7 +116,8 @@ xx(lvconvert,
"\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n",
alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG,
mirrorlog_ARG, mirrors_ARG, regionsize_ARG, snapshot_ARG, test_ARG, zero_ARG)
mirrorlog_ARG, mirrors_ARG, regionsize_ARG, repair_ARG, snapshot_ARG,
test_ARG, zero_ARG)
xx(lvcreate,
"Create a logical volume",

View File

@ -366,6 +366,60 @@ static int _insert_lvconvert_layer(struct cmd_context *cmd,
return 1;
}
static int _area_missing(struct lv_segment *lvseg, int s)
{
if (seg_type(lvseg, s) == AREA_LV) {
if (seg_lv(lvseg, s)->status & PARTIAL_LV)
return 1;
} else if (seg_type(lvseg, s) == AREA_PV) {
if (seg_pv(lvseg, s)->status & MISSING_PV)
return 1;
}
return 0;
}
/* FIXME we want to handle mirror stacks here... */
static int _count_failed_mirrors(struct logical_volume *lv)
{
struct lv_segment *lvseg;
int ret = 0;
int s;
dm_list_iterate_items(lvseg, &lv->segments) {
if (!seg_is_mirrored(lvseg))
return -1;
for(s = 0; s < lvseg->area_count; ++s) {
if (_area_missing(lvseg, s))
++ ret;
}
}
return ret;
}
static struct dm_list *_failed_pv_list(struct volume_group *vg)
{
struct dm_list *r;
struct pv_list *pvl, *new_pvl;
if (!(r = dm_pool_alloc(vg->vgmem, sizeof(*r)))) {
log_error("Allocation of list failed");
return_0;
}
dm_list_init(r);
dm_list_iterate_items(pvl, &vg->pvs) {
if (!(pvl->pv->status & MISSING_PV))
continue;
if (!(new_pvl = dm_pool_alloc(vg->vgmem, sizeof(*new_pvl)))) {
log_error("Unable to allocate physical volume list.");
return_0;
}
new_pvl->pv = pvl->pv;
dm_list_add(r, &new_pvl->list);
}
return r;
}
/* walk down the stacked mirror LV to the original mirror LV */
static struct logical_volume *_original_lv(struct logical_volume *lv)
{
@ -386,17 +440,26 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
unsigned corelog = 0;
struct logical_volume *original_lv;
int r = 0;
struct logical_volume *log_lv;
int failed_mirrors = 0, failed_log = 0;
struct dm_list *old_pvh, *remove_pvs = NULL;
seg = first_seg(lv);
existing_mirrors = lv_mirror_count(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)) {
!arg_count(cmd, corelog_ARG) && !arg_count(cmd, regionsize_ARG) &&
!arg_count(cmd, repair_ARG)) {
lp->need_polling = 1;
return 1;
}
if (arg_count(cmd, mirrors_ARG) && arg_count(cmd, repair_ARG)) {
log_error("You can only use one of -m, --repair.");
return 0;
}
/*
* Adjust required number of mirrors
*
@ -414,38 +477,59 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
else
lp->mirrors += 1;
/*
* 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))
corelog = 1;
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");
if (arg_count(cmd,repair_ARG)) {
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 0;
}
if ((failed_mirrors = _count_failed_mirrors(lv)) < 0)
return_0;
lp->mirrors -= failed_mirrors;
log_error("Mirror status: %d/%d legs failed.",
failed_mirrors, existing_mirrors);
old_pvh = lp->pvh;
if (!(lp->pvh = _failed_pv_list(lv->vg)))
return_0;
log_lv=first_seg(lv)->log_lv;
if (!log_lv || log_lv->status & PARTIAL_LV)
failed_log = corelog = 1;
} 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))
corelog = 1;
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;
}
corelog = 0;
} else if (!strcmp("core", mirrorlog))
corelog = 1;
else {
log_error("Unknown mirrorlog type: %s", mirrorlog);
return 0;
}
corelog = 0;
} else if (!strcmp("core", mirrorlog))
corelog = 1;
else {
log_error("Unknown mirrorlog type: %s", mirrorlog);
return 0;
}
log_verbose("Setting logging type to %s", mirrorlog);
log_verbose("Setting logging type to %s", mirrorlog);
}
/*
* Region size must not change on existing mirrors
@ -457,6 +541,18 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
return 0;
}
/*
* FIXME This check used to precede mirror->mirror conversion
* but didn't affect mirror->linear or linear->mirror. I do
* not understand what is its intention, in fact.
*/
if (dm_list_size(&lv->segments) != 1) {
log_error("Logical volume %s has multiple "
"mirror segments.", lv->name);
return 0;
}
restart:
/*
* Converting from mirror to linear
*/
@ -466,17 +562,24 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
lv->name);
return 1;
}
if (!lv_remove_mirrors(cmd, lv, existing_mirrors - 1, 1,
lp->pv_count ? lp->pvh : NULL, 0))
return_0;
goto commit_changes;
}
/*
* Converting from linear to mirror
* Downconversion.
*/
if (!(lv->status & MIRRORED)) {
if (lp->mirrors < existing_mirrors) {
/* Reduce number of mirrors */
if (arg_count(cmd, repair_ARG) || 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))
return_0;
} else if (!(lv->status & MIRRORED)) {
/*
* Converting from linear to mirror
*/
/* FIXME Share code with lvcreate */
/* FIXME Why is this restriction here? Fix it! */
@ -487,6 +590,11 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
}
}
/*
* FIXME should we give not only lp->pvh, but also all PVs
* 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,
@ -497,46 +605,7 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
return_0;
if (lp->wait_completion)
lp->need_polling = 1;
goto commit_changes;
}
/*
* Converting from mirror to mirror with different leg count,
* or different log type.
*/
if (dm_list_size(&lv->segments) != 1) {
log_error("Logical volume %s has multiple "
"mirror segments.", lv->name);
return 0;
}
if (lp->mirrors == existing_mirrors) {
/*
* Convert Mirror log type
*/
original_lv = _original_lv(lv);
if (!first_seg(original_lv)->log_lv && !corelog) {
if (!add_mirror_log(cmd, original_lv, 1,
adjusted_mirror_region_size(
lv->vg->extent_size,
lv->le_count,
lp->region_size),
lp->pvh, lp->alloc))
return_0;
} else if (first_seg(original_lv)->log_lv && corelog) {
if (!remove_mirror_log(cmd, original_lv,
lp->pv_count ? lp->pvh : NULL))
return_0;
} else {
/* No change */
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;
}
} else if (lp->mirrors > existing_mirrors) {
} else if (lp->mirrors > existing_mirrors || failed_mirrors) {
if (lv->status & MIRROR_NOTSYNCED) {
log_error("Not adding mirror to mirrored LV "
"without initial resync");
@ -578,15 +647,36 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l
return_0;
lv->status |= CONVERTING;
lp->need_polling = 1;
} else {
/* Reduce number of mirrors */
if (!lv_remove_mirrors(cmd, lv, existing_mirrors - lp->mirrors,
corelog ? 1U : 0U,
lp->pv_count ? lp->pvh : NULL, 0))
return_0;
}
commit_changes:
if (lp->mirrors == existing_mirrors) {
/*
* Convert Mirror log type
*/
original_lv = _original_lv(lv);
if (!first_seg(original_lv)->log_lv && !corelog) {
if (!add_mirror_log(cmd, original_lv, 1,
adjusted_mirror_region_size(
lv->vg->extent_size,
lv->le_count,
lp->region_size),
lp->pvh, lp->alloc))
return_0;
} else if (first_seg(original_lv)->log_lv && corelog) {
if (!remove_mirror_log(cmd, original_lv,
lp->pv_count ? lp->pvh : NULL))
return_0;
} else {
/* No change */
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;
}
}
log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
if (!vg_write(lv->vg))
@ -610,6 +700,17 @@ commit_changes:
goto out;
}
if (failed_log || failed_mirrors) {
lp->pvh = old_pvh;
if (failed_log)
failed_log = corelog = 0;
lp->mirrors += failed_mirrors;
failed_mirrors = 0;
existing_mirrors = lv_mirror_count(lv);
/* Now replace missing devices. */
goto restart;
}
if (!lp->need_polling)
log_print("Logical volume %s converted.", lv->name);