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

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.
This commit is contained in:
Jonathan Earl Brassow 2010-03-26 22:15:43 +00:00
parent 217cb1aa84
commit 7a369d3704
16 changed files with 597 additions and 280 deletions

View File

@ -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.

View File

@ -148,6 +148,11 @@ static int _remove_failed_devices(const char *device)
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 */

View File

@ -703,6 +703,7 @@ int monitor_dev_for_events(struct cmd_context *cmd,
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 @@ int monitor_dev_for_events(struct cmd_context *cmd,
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);

View File

@ -68,7 +68,8 @@ int lv_add_mirror_lvs(struct logical_volume *lv,
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);

View File

@ -1635,14 +1635,32 @@ int lv_add_mirror_lvs(struct logical_volume *lv,
/*
* 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,

View File

@ -709,8 +709,8 @@ static int _split_mirror_images(struct logical_volume *lv,
*
* 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 @@ static int _remove_mirror_images(struct logical_volume *lv,
{
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 @@ static int _remove_mirror_images(struct logical_volume *lv,
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 @@ static int _remove_mirror_images(struct logical_volume *lv,
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 @@ int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirr
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 @@ static int _create_mimage_lvs(struct alloc_handle *ah,
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 @@ static struct logical_volume *_create_mirror_log(struct logical_volume *lv,
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 @@ static struct logical_volume *_set_up_mirror_log(struct cmd_context *cmd,
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 @@ static struct logical_volume *_set_up_mirror_log(struct cmd_context *cmd,
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 @@ int add_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
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 @@ int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv,
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,33 +1795,8 @@ int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv,
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.");
if (!_form_mirror(cmd, ah, lv, mirrors, region_size, 0))
goto out_remove_log;
}
if (!_create_mimage_lvs(ah, mirrors, lv, img_lvs))
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;

View File

@ -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 @@ from the data being mirrored.
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".

View File

@ -13,7 +13,7 @@ lvcreate \- create a logical volume in an existing volume group
{\-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 @@ intensive initial sync of an empty device.
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.

View File

@ -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 @@ repair() {
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

View File

@ -69,5 +69,5 @@ 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 -y -i 1 --repair $vg/mirror2
echo 'y' | lvconvert -y -i 1 --repair $vg/mirror2
vgs

View File

@ -25,9 +25,18 @@ aux prepare_pvs 2
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

View File

@ -280,6 +280,38 @@ mimages_are_redundant_ $vg $lv1
mirrorlog_is_on_ $vg/$lv1 $dev3
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

View File

@ -21,11 +21,11 @@ not lvconvert -m2 $vg/lv
# 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

View File

@ -96,7 +96,7 @@ xx(lvconvert,
"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 @@ xx(lvcreate,
"\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"

View File

@ -647,31 +647,72 @@ static void _lvconvert_mirrors_repair_ask(struct cmd_context *cmd,
}
}
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))
return_0;
} else if (!_using_corelog(lv) && !log_count) {
if (!remove_mirror_log(cmd, original_lv,
lp->pv_count ? lp->pvh : NULL))
region_size, operable_pvs, lp->alloc))
return_0;
return 1;
}
return 1;
/* Remove an existing log completely */
if (!log_count) {
if (!remove_mirror_log(cmd, original_lv, operable_pvs))
return_0;
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 void _remove_missing_empty_pv(struct volume_group *vg, struct dm_list *re
}
}
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;
const char *mirrorlog;
*old_mimage_count = lv_mirror_count(lv);
*old_log_count = _get_log_count(lv);
seg = first_seg(lv);
existing_mirrors = lv_mirror_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;
}
/* 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 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.
* 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 ((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;
if (*old_mimage_count != *new_mimage_count)
log_verbose("Adjusting mirror image count of %s", lv->name);
/*
* 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 (arg_count(cmd, corelog_ARG))
*new_log_count = 0;
mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
!*new_log_count ? "core" : DEFAULT_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;
}
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;
mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
!log_count ? "core" : DEFAULT_MIRRORLOG);
if (strcmp("core", mirrorlog) && !log_count) {
log_error("--mirrorlog disk and --corelog "
"are incompatible");
return 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;
}
log_verbose("Setting logging type to %s", mirrorlog);
}
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 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv
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;
}
region_size = adjusted_mirror_region_size(lv->vg->extent_size,
lv->le_count,
lp->region_size);
if (!operable_pvs)
operable_pvs = lp->pvh;
seg = first_seg(lv);
/*
* Downconversion.
* Up-convert from linear to mirror
*/
if (lp->mirrors < existing_mirrors) {
/* Reduce number of mirrors */
if (repair || lp->pv_count)
remove_pvs = lp->pvh;
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 (lp->mirrors > 1 &&
!_lv_update_log_type(cmd, lp, lv, log_count))
return_0;
} else if (!(lv->status & MIRRORED)) {
/*
* Converting 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 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv
* 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 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv
* 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 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv
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 @@ static int _lvconvert_mirrors(struct cmd_context *cmd, struct logical_volume *lv
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);
/*
* Ignore failure in upconversion if this is a policy-driven
* action. If we got this far, only unexpected failures are
* reported.
*/
if (arg_count(cmd, use_policies_ARG))
failure_code = 1;
/* Now replace missing devices. */
if (replace_log || replace_mirrors)
goto restart;
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)) {
/*
* If we are up-converting the log from linear to
* mirrored, then we must use '_lvconvert_mirrors_aux'
*/
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,

View File

@ -337,12 +337,14 @@ static int _read_mirror_params(struct lvcreate_params *lp,
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;