From 3318c41356430c4570f5525ac2a11f27e982724e Mon Sep 17 00:00:00 2001 From: Jonathan Earl Brassow Date: Fri, 26 Mar 2010 22:15:43 +0000 Subject: [PATCH] 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. --- WHATS_NEW | 1 + .../dmeventd/plugins/mirror/dmeventd_mirror.c | 5 + lib/activate/activate.c | 11 + lib/metadata/lv_alloc.h | 3 +- lib/metadata/lv_manip.c | 28 +- lib/metadata/mirror.c | 129 ++-- man/lvconvert.8.in | 3 +- man/lvcreate.8.in | 7 +- test/t-lvconvert-repair-policy.sh | 21 +- test/t-lvconvert-repair.sh | 2 +- test/t-lvcreate-operation.sh | 13 +- test/t-mirror-lvconvert.sh | 32 + test/t-snapshots-of-mirrors.sh | 8 +- tools/commands.h | 4 +- tools/lvconvert.c | 580 ++++++++++++------ tools/lvcreate.c | 14 +- 16 files changed, 589 insertions(+), 272 deletions(-) diff --git a/WHATS_NEW b/WHATS_NEW index 32748f890..bf6c8e69d 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -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. diff --git a/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c index c715e7572..e5d81ee20 100644 --- a/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c +++ b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c @@ -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 */ diff --git a/lib/activate/activate.c b/lib/activate/activate.c index eb94ac011..1844cea79 100644 --- a/lib/activate/activate.c +++ b/lib/activate/activate.c @@ -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); diff --git a/lib/metadata/lv_alloc.h b/lib/metadata/lv_alloc.h index ac4053997..994da612a 100644 --- a/lib/metadata/lv_alloc.h +++ b/lib/metadata/lv_alloc.h @@ -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); diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index 613175d40..97df5a710 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -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, diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c index fe563fc33..97b498914 100644 --- a/lib/metadata/mirror.c +++ b/lib/metadata/mirror.c @@ -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; diff --git a/man/lvconvert.8.in b/man/lvconvert.8.in index d595dc277..45ea2c6aa 100644 --- a/man/lvconvert.8.in +++ b/man/lvconvert.8.in @@ -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". diff --git a/man/lvcreate.8.in b/man/lvcreate.8.in index 56df7226d..3c36b9721 100644 --- a/man/lvcreate.8.in +++ b/man/lvcreate.8.in @@ -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. diff --git a/test/t-lvconvert-repair-policy.sh b/test/t-lvconvert-repair-policy.sh index 046f3dcdd..96305ff4d 100644 --- a/test/t-lvconvert-repair-policy.sh +++ b/test/t-lvconvert-repair-policy.sh @@ -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 diff --git a/test/t-lvconvert-repair.sh b/test/t-lvconvert-repair.sh index db26b3f49..796833b6f 100644 --- a/test/t-lvconvert-repair.sh +++ b/test/t-lvconvert-repair.sh @@ -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 diff --git a/test/t-lvcreate-operation.sh b/test/t-lvcreate-operation.sh index e76f29446..15cd99554 100644 --- a/test/t-lvcreate-operation.sh +++ b/test/t-lvcreate-operation.sh @@ -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 diff --git a/test/t-mirror-lvconvert.sh b/test/t-mirror-lvconvert.sh index 02dfdfeef..016da4d87 100755 --- a/test/t-mirror-lvconvert.sh +++ b/test/t-mirror-lvconvert.sh @@ -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 diff --git a/test/t-snapshots-of-mirrors.sh b/test/t-snapshots-of-mirrors.sh index 86a28c81d..7ee8249b8 100644 --- a/test/t-snapshots-of-mirrors.sh +++ b/test/t-snapshots-of-mirrors.sh @@ -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 diff --git a/tools/commands.h b/tools/commands.h index 58d6d7b9f..9b611de20 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -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" diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 1f78a77ee..7ce313565 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -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)) + region_size, operable_pvs, lp->alloc)) return_0; - } else if (!_using_corelog(lv) && !log_count) { - if (!remove_mirror_log(cmd, original_lv, - lp->pv_count ? lp->pvh : NULL)) + return 1; + } + + /* Remove an existing log completely */ + if (!log_count) { + if (!remove_mirror_log(cmd, original_lv, operable_pvs)) return_0; + return 1; } - 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; - - seg = first_seg(lv); - existing_mirrors = lv_mirror_count(lv); + const char *mirrorlog; + *old_mimage_count = lv_mirror_count(lv); + *old_log_count = _get_log_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; } - /* - * 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. - */ - 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; + /* 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 (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; + /* + * 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 (*old_mimage_count != *new_mimage_count) + log_verbose("Adjusting mirror image count of %s", lv->name); - mirrorlog = arg_str_value(cmd, mirrorlog_ARG, - !log_count ? "core" : DEFAULT_MIRRORLOG); + /* + * 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 (strcmp("core", mirrorlog) && !log_count) { - log_error("--mirrorlog disk and --corelog " - "are incompatible"); - return 0; - } + if (arg_count(cmd, corelog_ARG)) + *new_log_count = 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; - } + mirrorlog = arg_str_value(cmd, mirrorlog_ARG, + !*new_log_count ? "core" : DEFAULT_MIRRORLOG); - log_verbose("Setting logging type to %s", 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; } + 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; } - /* - * Downconversion. - */ - if (lp->mirrors < existing_mirrors) { - /* Reduce number of mirrors */ - if (repair || lp->pv_count) - remove_pvs = lp->pvh; + region_size = adjusted_mirror_region_size(lv->vg->extent_size, + lv->le_count, + lp->region_size); - 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 (!operable_pvs) + operable_pvs = lp->pvh; - 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 - */ + seg = first_seg(lv); + /* + * Up-convert 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; + 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 (replace_mirrors) - lp->mirrors += failed_mirrors; - failed_mirrors = 0; - existing_mirrors = lv_mirror_count(lv); + } + 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)) { /* - * Ignore failure in upconversion if this is a policy-driven - * action. If we got this far, only unexpected failures are - * reported. + * If we are up-converting the log from linear to + * mirrored, then we must use '_lvconvert_mirrors_aux' */ - if (arg_count(cmd, use_policies_ARG)) - failure_code = 1; - /* Now replace missing devices. */ - if (replace_log || replace_mirrors) - goto restart; + 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, diff --git a/tools/lvcreate.c b/tools/lvcreate.c index 10b5aa591..a2480cdd1 100644 --- a/tools/lvcreate.c +++ b/tools/lvcreate.c @@ -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; -- 2.43.5