diff --git a/lib/activate/activate.c b/lib/activate/activate.c index c6c6af7a8..2128c4198 100644 --- a/lib/activate/activate.c +++ b/lib/activate/activate.c @@ -475,6 +475,28 @@ int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, return r; } +/* + * Returns 1 if percent set, else 0 on failure. + */ +int lv_check_transient(struct logical_volume *lv) +{ + int r; + struct dev_manager *dm; + + if (!activation()) + return 0; + + if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name))) + return_0; + + if (!(r = dev_manager_transient(dm, lv))) + stack; + + dev_manager_destroy(dm); + + return r; +} + /* * Returns 1 if percent set, else 0 on failure. */ diff --git a/lib/activate/activate.h b/lib/activate/activate.h index cae962e58..6a668fe80 100644 --- a/lib/activate/activate.h +++ b/lib/activate/activate.h @@ -78,6 +78,7 @@ int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s, int *activate_lv); +int lv_check_transient(struct logical_volume *lv); /* * Returns 1 if percent has been set, else 0. */ diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c index f64560462..bdd403209 100644 --- a/lib/activate/dev_manager.c +++ b/lib/activate/dev_manager.c @@ -543,6 +543,68 @@ static int _percent(struct dev_manager *dm, const char *name, const char *dlid, return 0; } +int dev_manager_transient(struct dev_manager *dm, struct logical_volume *lv) +{ + int r = 0; + struct dm_task *dmt; + struct dm_info info; + void *next = NULL; + uint64_t start, length; + char *type = NULL; + char *params = NULL; + char *dlid = NULL; + const struct dm_list *segh = &lv->segments; + struct lv_segment *seg = NULL; + + if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, NULL))) + return_0; + + if (!(dmt = _setup_task(0, dlid, NULL, DM_DEVICE_STATUS, 0, 0))) + return_0; + + if (!dm_task_no_open_count(dmt)) + log_error("Failed to disable open_count"); + + if (!dm_task_run(dmt)) + goto_out; + + if (!dm_task_get_info(dmt, &info) || !info.exists) + goto_out; + + do { + next = dm_get_next_target(dmt, next, &start, &length, &type, + ¶ms); + if (lv) { + if (!(segh = dm_list_next(&lv->segments, segh))) { + log_error("Number of segments in active LV %s " + "does not match metadata", lv->name); + goto out; + } + seg = dm_list_item(segh, struct lv_segment); + } + + if (!type || !params) + continue; + + if (seg->segtype->ops->check_transient_status && + !seg->segtype->ops->check_transient_status(seg, params)) + goto_out; + + } while (next); + + if (lv && (segh = dm_list_next(&lv->segments, segh))) { + log_error("Number of segments in active LV %s does not " + "match metadata", lv->name); + goto out; + } + + r = 1; + + out: + dm_task_destroy(dmt); + return r; +} + /* * dev_manager implementation. */ diff --git a/lib/activate/dev_manager.h b/lib/activate/dev_manager.h index e946aafb8..614db859d 100644 --- a/lib/activate/dev_manager.h +++ b/lib/activate/dev_manager.h @@ -57,6 +57,7 @@ int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv); int dev_manager_preload(struct dev_manager *dm, struct logical_volume *lv, int *flush_required); int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv); +int dev_manager_transient(struct dev_manager *dm, struct logical_volume *lv); int dev_manager_mknodes(const struct logical_volume *lv); diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 258f221d5..dbac0f2f5 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -754,7 +754,8 @@ int lv_split_mirror_images(struct logical_volume *lv, const char *split_lv_name, uint32_t split_count, struct dm_list *removable_pvs); int lv_remove_mirrors(struct cmd_context *cmd, struct logical_volume *lv, uint32_t mirrors, uint32_t log_count, - struct dm_list *pvs, uint64_t status_mask); + int (*is_removable)(struct logical_volume *, void *), + void *removable_baton, uint64_t status_mask); int is_temporary_mirror_layer(const struct logical_volume *lv); struct logical_volume * find_temporary_mirror(const struct logical_volume *lv); @@ -769,7 +770,8 @@ int add_mirrors_to_segments(struct cmd_context *cmd, struct logical_volume *lv, struct dm_list *allocatable_pvs, alloc_policy_t alloc); int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors, - struct dm_list *removable_pvs, unsigned remove_log); + int (*is_removable)(struct logical_volume *, void *), + void *removable_baton, unsigned remove_log); int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv, uint32_t mirrors, uint32_t stripes, uint32_t stripe_size, uint32_t region_size, struct dm_list *allocatable_pvs, alloc_policy_t alloc, diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index b49f975d5..d76a91024 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -2117,7 +2117,7 @@ static int _lv_mark_if_partial(struct logical_volume *lv) * propagated transitively, so LVs referencing other LVs are marked * partial as well, if any of their referenced LVs are marked partial. */ -static int _vg_mark_partial_lvs(struct volume_group *vg) +int vg_mark_partial_lvs(struct volume_group *vg) { struct logical_volume *lv; struct lv_list *lvl; @@ -2654,7 +2654,7 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, if (vg_missing_pv_count(correct_vg)) { log_verbose("There are %d physical volumes missing.", vg_missing_pv_count(correct_vg)); - _vg_mark_partial_lvs(correct_vg); + vg_mark_partial_lvs(correct_vg); } *consistent = 1; return correct_vg; @@ -2945,7 +2945,7 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, if (vg_missing_pv_count(correct_vg)) { log_verbose("There are %d physical volumes missing.", vg_missing_pv_count(correct_vg)); - _vg_mark_partial_lvs(correct_vg); + vg_mark_partial_lvs(correct_vg); } if ((correct_vg->status & PVMOVE) && !pvmove_mode()) { diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 7177070d5..3a3979d8d 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -383,5 +383,7 @@ struct id pv_vgid(const struct physical_volume *pv); struct physical_volume *pv_by_path(struct cmd_context *cmd, const char *pv_name); int add_pv_to_vg(struct volume_group *vg, const char *pv_name, struct physical_volume *pv); +int vg_mark_partial_lvs(struct volume_group *vg); +int is_mirror_image_removable(struct logical_volume *mimage_lv, void *baton); #endif diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c index 7ea26d5f0..11777c0ed 100644 --- a/lib/metadata/mirror.c +++ b/lib/metadata/mirror.c @@ -435,14 +435,17 @@ struct logical_volume *detach_mirror_log(struct lv_segment *mirrored_seg) } /* Check if mirror image LV is removable with regard to given removable_pvs */ -static int _is_mirror_image_removable(struct logical_volume *mimage_lv, - struct dm_list *removable_pvs) +int is_mirror_image_removable(struct logical_volume *mimage_lv, void *baton) { struct physical_volume *pv; struct lv_segment *seg; int pv_found; struct pv_list *pvl; uint32_t s; + struct dm_list *removable_pvs = baton; + + if (!baton || dm_list_empty(removable_pvs)) + return 1; dm_list_iterate_items(seg, &mimage_lv->segments) { for (s = 0; s < seg->area_count; s++) { @@ -508,7 +511,7 @@ static int _move_removable_mimages_to_end(struct logical_volume *lv, sub_lv = seg_lv(mirrored_seg, i); if (!is_temporary_mirror_layer(sub_lv) && - _is_mirror_image_removable(sub_lv, removable_pvs)) { + is_mirror_image_removable(sub_lv, removable_pvs)) { if (!shift_mirror_images(mirrored_seg, i)) return_0; count--; @@ -754,13 +757,13 @@ static int _split_mirror_images(struct logical_volume *lv, */ static int _remove_mirror_images(struct logical_volume *lv, uint32_t num_removed, - struct dm_list *removable_pvs, + int (*is_removable)(struct logical_volume *, void *), + void *removable_baton, unsigned remove_log, unsigned collapse, uint32_t *removed) { uint32_t m; int32_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; @@ -770,9 +773,6 @@ 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; @@ -781,38 +781,32 @@ static int _remove_mirror_images(struct logical_volume *lv, old_area_count, old_area_count - num_removed, remove_log ? " and no log volume" : ""); - if (collapse && - (removable_pvs_specified || (old_area_count - num_removed != 1))) { + if (collapse && (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_specified) { - for (s = mirrored_seg->area_count - 1; - s >= 0 && old_area_count - new_area_count < num_removed; - s--) { - sub_lv = seg_lv(mirrored_seg, s); - - if (!is_temporary_mirror_layer(sub_lv) && - _is_mirror_image_removable(sub_lv, removable_pvs)) { - /* - * Check if the user is trying to pull the - * primary mirror image when the mirror is - * not in-sync. - */ - if ((s == 0) && !_mirrored_lv_in_sync(lv) && - !(lv->status & PARTIAL_LV)) { - log_error("Unable to remove primary mirror image while mirror is not in-sync"); - return_0; - } - if (!shift_mirror_images(mirrored_seg, s)) - return_0; - new_area_count--; + for (s = mirrored_seg->area_count - 1; + s >= 0 && old_area_count - new_area_count < num_removed; + s--) { + sub_lv = seg_lv(mirrored_seg, s); + if (!is_temporary_mirror_layer(sub_lv) && + is_removable(sub_lv, removable_baton)) { + /* + * Check if the user is trying to pull the + * primary mirror image when the mirror is + * not in-sync. + */ + if ((s == 0) && !_mirrored_lv_in_sync(lv) && + !(lv->status & PARTIAL_LV)) { + log_error("Unable to remove primary mirror image while mirror is not in-sync"); + return_0; } + if (!shift_mirror_images(mirrored_seg, s)) + return_0; + new_area_count--; } - if (num_removed && old_area_count == new_area_count) - return 1; } /* @@ -822,6 +816,9 @@ static int _remove_mirror_images(struct logical_volume *lv, */ new_area_count = old_area_count - num_removed; + if (num_removed && old_area_count == new_area_count) + return 1; + /* Remove mimage LVs from the segment */ dm_list_init(&tmp_orphan_lvs); for (m = new_area_count; m < mirrored_seg->area_count; m++) { @@ -956,7 +953,8 @@ static int _remove_mirror_images(struct logical_volume *lv, * Remove the number of mirror images from the LV */ int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors, - struct dm_list *removable_pvs, unsigned remove_log) + int (*is_removable)(struct logical_volume *, void *), + void *removable_baton, unsigned remove_log) { uint32_t num_removed, removed_once, r; uint32_t existing_mirrors = lv_mirror_count(lv); @@ -972,7 +970,8 @@ int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors, removed_once = first_seg(next_lv)->area_count - 1; if (!_remove_mirror_images(next_lv, removed_once, - removable_pvs, remove_log, 0, &r)) + is_removable, removable_baton, + remove_log, 0, &r)) return_0; if (r < removed_once) { @@ -999,6 +998,11 @@ int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors, return 1; } +static int _no_removable_images(struct logical_volume *lv __attribute((unused)), + void *baton __attribute((unused))) { + return 0; +} + /* * Collapsing temporary mirror layers. * @@ -1031,7 +1035,7 @@ int collapse_mirrored_lv(struct logical_volume *lv) if (!_remove_mirror_images(mirror_seg->lv, mirror_seg->area_count - 1, - NULL, 1, 1, NULL)) { + _no_removable_images, NULL, 1, 1, NULL)) { log_error("Failed to release mirror images"); return 0; } @@ -1156,7 +1160,8 @@ int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirr init_mirror_in_sync(in_sync); r = _remove_mirror_images(mirrored_seg->lv, old_num_mirrors - num_mirrors, - removable_pvs, remove_log, 0, NULL); + is_mirror_image_removable, removable_pvs, + remove_log, 0, NULL); if (!r) /* Unable to remove bad devices */ return 0; @@ -1549,7 +1554,7 @@ int remove_mirror_log(struct cmd_context *cmd, } if (!remove_mirror_images(lv, lv_mirror_count(lv), - removable_pvs, 1U)) + is_mirror_image_removable, removable_pvs, 1U)) return_0; return 1; @@ -1929,7 +1934,9 @@ int lv_split_mirror_images(struct logical_volume *lv, const char *split_name, */ int lv_remove_mirrors(struct cmd_context *cmd __attribute((unused)), struct logical_volume *lv, - uint32_t mirrors, uint32_t log_count, struct dm_list *pvs, + uint32_t mirrors, uint32_t log_count, + int (*is_removable)(struct logical_volume *, void *), + void *removable_baton, uint64_t status_mask) { uint32_t new_mirrors; @@ -1957,7 +1964,8 @@ int lv_remove_mirrors(struct cmd_context *cmd __attribute((unused)), if (seg_type(seg, 0) == AREA_LV && seg_lv(seg, 0)->status & MIRROR_IMAGE) return remove_mirror_images(lv, new_mirrors + 1, - pvs, log_count ? 1U : 0); + is_removable, removable_baton, + log_count ? 1U : 0); /* MIRROR_BY_SEG */ if (log_count) { diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h index 89e2e9884..d15df8bb7 100644 --- a/lib/metadata/segtype.h +++ b/lib/metadata/segtype.h @@ -82,6 +82,7 @@ struct segtype_handler { struct dm_tree_node *node, uint64_t len, uint32_t *pvmove_mirror_count); int (*target_status_compatible) (const char *type); + int (*check_transient_status) (struct lv_segment *seg, char *params); int (*target_percent) (void **target_state, percent_range_t *percent_range, struct dm_pool * mem, diff --git a/lib/mirror/mirrored.c b/lib/mirror/mirrored.c index ee0ee6495..69452ebd3 100644 --- a/lib/mirror/mirrored.c +++ b/lib/mirror/mirrored.c @@ -239,6 +239,117 @@ static int _mirrored_target_percent(void **target_state, return 1; } +static int _mirrored_transient_status(struct lv_segment *seg, char *params) +{ + int i, j; + struct logical_volume *lv = seg->lv; + struct lvinfo info; + char *p = NULL; + char **args, **log_args; + struct logical_volume **images; + struct logical_volume *log; + int num_devs, log_argc; + int failed = 0; + char *status; + + log_error("Mirrored transient status: \"%s\"", params); + + /* number of devices */ + if (!dm_split_words(params, 1, 0, &p)) + return_0; + + if (!(num_devs = atoi(p))) + return_0; + + p += strlen(p) + 1; + + if (num_devs > DEFAULT_MIRROR_MAX_IMAGES) { + log_error("Unexpectedly many (%d) mirror images in %s.", + num_devs, lv->name); + return_0; + } + + args = alloca((num_devs + 5) * sizeof(char *)); + images = alloca(num_devs * sizeof(struct logical_volume *)); + + if (dm_split_words(p, num_devs + 4, 0, args) < num_devs + 4) + return_0; + + log_argc = atoi(args[3 + num_devs]); + log_args = alloca(log_argc * sizeof(char *)); + + if (log_argc > 16) { + log_error("Unexpectedly many (%d) log arguments in %s.", + log_argc, lv->name); + return_0; + } + + + if (dm_split_words(args[3 + num_devs] + strlen(args[3 + num_devs]) + 1, + log_argc, 0, log_args) < log_argc) + return_0; + + if (num_devs != seg->area_count) { + log_error("Active mirror has a wrong number of mirror images!"); + log_error("Metadata says %d, kernel says %d.", seg->area_count, num_devs); + return_0; + } + + if (!strcmp(log_args[0], "disk")) { + char buf[32]; + log = first_seg(lv)->log_lv; + lv_info(lv->vg->cmd, log, &info, 0, 0); + log_debug("Found mirror log at %d:%d", info.major, info.minor); + sprintf(buf, "%d:%d", info.major, info.minor); + if (strcmp(buf, log_args[1])) { + log_error("Mirror log mismatch. Metadata says %s, kernel says %s.", + buf, log_args[1]); + return_0; + } + log_very_verbose("Status of log (%s): %s", buf, log_args[2]); + if (log_args[2][0] != 'A') { + log->status |= PARTIAL_LV; + ++failed; + } + } + + for (i = 0; i < num_devs; ++i) + images[i] = NULL; + + for (i = 0; i < seg->area_count; ++i) { + char buf[32]; + lv_info(lv->vg->cmd, seg_lv(seg, i), &info, 0, 0); + log_debug("Found mirror leg at %d:%d", info.major, info.minor); + sprintf(buf, "%d:%d", info.major, info.minor); + for (j = 0; j < num_devs; ++j) { + if (!strcmp(buf, args[j])) { + log_debug("Match: metadata image %d matches kernel image %d", i, j); + images[j] = seg_lv(seg, i); + } + } + } + + status = args[2 + num_devs]; + + for (i = 0; i < num_devs; ++i) { + if (!images[i]) { + log_error("Failed to find image %d (%s).", i, args[i]); + return_0; + } + log_very_verbose("Status of image %d: %c", i, status[i]); + if (status[i] != 'A') { + images[i]->status |= PARTIAL_LV; + ++failed; + } + } + + /* update PARTIAL_LV flags across the VG */ + if (failed) + vg_mark_partial_lvs(lv->vg); + + return 1; +} + static int _add_log(struct dm_pool *mem, struct lv_segment *seg, struct dm_tree_node *node, uint32_t area_count, uint32_t region_size) { @@ -564,6 +675,7 @@ static struct segtype_handler _mirrored_ops = { .add_target_line = _mirrored_add_target_line, .target_percent = _mirrored_target_percent, .target_present = _mirrored_target_present, + .check_transient_status = _mirrored_transient_status, #ifdef DMEVENTD .target_monitored = _target_monitored, .target_monitor_events = _target_monitor_events, diff --git a/test/t-lvconvert-repair-policy.sh b/test/t-lvconvert-repair-policy.sh index 8ba2654e0..1f3121590 100644 --- a/test/t-lvconvert-repair-policy.sh +++ b/test/t-lvconvert-repair-policy.sh @@ -32,40 +32,49 @@ lvcreate -m 1 -L 1 -n mirror $vg lvchange -a n $vg/mirror # Fail a leg of a mirror. -disable_dev $dev1 +aux disable_dev $dev1 lvchange --partial -a y $vg/mirror repair 'activation { mirror_image_fault_policy = "remove" }' check linear $vg mirror -cleanup $dev1 +aux cleanup $dev1 # Fail a leg of a mirror. # Expected result: Mirror (leg replaced) -disable_dev $dev1 +aux disable_dev $dev1 repair 'activation { mirror_image_fault_policy = "replace" }' check mirror $vg mirror lvs | grep mirror_mlog -cleanup $dev1 +aux cleanup $dev1 # Fail a leg of a mirror (use old name for policy specification) # Expected result: Mirror (leg replaced) -disable_dev $dev1 +aux disable_dev $dev1 repair 'activation { mirror_device_fault_policy = "replace" }' check mirror $vg mirror lvs | grep mirror_mlog -cleanup $dev1 +aux cleanup $dev1 # Fail a leg of a mirror w/ no available spare # Expected result: 2-way with corelog -disable_dev $dev2 $dev4 +aux disable_dev $dev2 $dev4 repair 'activation { mirror_image_fault_policy = "replace" }' check mirror $vg mirror lvs | not grep mirror_mlog -cleanup $dev2 $dev4 +aux cleanup $dev2 $dev4 # Fail the log device of a mirror w/ no available spare # Expected result: mirror w/ corelog -disable_dev $dev3 $dev4 -lvconvert --repair --use-policies --config 'activation { mirror_image_fault_policy = "replace" }' $vg/mirror +aux disable_dev $dev3 $dev4 +repair 'activation { mirror_image_fault_policy = "replace" }' $vg/mirror check mirror $vg mirror lvs | not grep mirror_mlog +aux cleanup $dev3 $dev4 + +# Fail the log device with a remove policy +# Expected result: mirror w/ corelog +lvchange -a y $vg/mirror +aux disable_dev $dev3 $dev4 +repair 'activation { mirror_log_fault_policy = "remove" }' +check mirror $vg mirror core +lvs | not grep mirror_mlog cleanup $dev3 $dev4 diff --git a/test/t-lvconvert-repair-transient.sh b/test/t-lvconvert-repair-transient.sh new file mode 100644 index 000000000..3d4fd1a89 --- /dev/null +++ b/test/t-lvconvert-repair-transient.sh @@ -0,0 +1,26 @@ +#!/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 5 + +# fail multiple devices + +lvcreate -m 3 --ig -L 1 -n 4way $vg +disable_dev $dev2 $dev4 +mkfs.ext3 $DM_DEV_DIR/$vg/4way +enable_dev $dev2 $dev4 +echo n | lvconvert --repair $vg/4way 2>&1 | tee 4way.out +lvs -a -o +devices | not grep unknown +vgreduce --removemissing $vg +check mirror $vg 4way +lvchange -a n $vg/4way diff --git a/test/t-lvconvert-repair.sh b/test/t-lvconvert-repair.sh index b8902f516..bce8b5359 100644 --- a/test/t-lvconvert-repair.sh +++ b/test/t-lvconvert-repair.sh @@ -11,38 +11,59 @@ . ./test-utils.sh -prepare_vg 5 # fail multiple devices -lvcreate -m 3 --ig -L 1 -n 4way $vg +aux prepare_vg 5 +lvcreate -m 3 --ig -L 1 -n 4way $vg $dev1 $dev2 $dev3 $dev4 $dev5:0 disable_dev $dev2 $dev4 echo n | lvconvert --repair $vg/4way 2>&1 | tee 4way.out lvs -a -o +devices | not grep unknown vgreduce --removemissing $vg enable_dev $dev2 $dev4 -check mirror $vg 4way -lvchange -a n $vg/4way - -vgremove -ff $vg -vgcreate -c n $vg $dev1 $dev2 $dev3 $dev4 +check mirror $vg 4way $dev5 +aux prepare_vg 5 lvcreate -m 2 --ig -L 1 -n 3way $vg disable_dev $dev1 $dev2 echo n | lvconvert --repair $vg/3way +check linear $vg 3way lvs -a -o +devices | not grep unknown +lvs -a -o +devices | not grep mlog +dmsetup ls | grep $PREFIX | not grep mlog vgreduce --removemissing $vg enable_dev $dev1 $dev2 check linear $vg 3way -lvchange -a n $vg/3way + +# fail just log and get it removed + +aux prepare_vg 5 +lvcreate -m 2 --ig -L 1 -n 3way $vg $dev1 $dev2 $dev3 $dev4:0 +disable_dev $dev4 +echo n | lvconvert --repair $vg/3way +check mirror $vg 3way core +lvs -a -o +devices | not grep unknown +lvs -a -o +devices | not grep mlog +dmsetup ls | grep $PREFIX | not grep mlog +vgreduce --removemissing $vg +enable_dev $dev4 + +aux prepare_vg 5 +lvcreate -m 1 --ig -L 1 -n 2way $vg $dev1 $dev2 $dev3:0 +disable_dev $dev3 +echo n | lvconvert --repair $vg/2way +check mirror $vg 2way core +lvs -a -o +devices | not grep unknown +lvs -a -o +devices | not grep mlog +vgreduce --removemissing $vg +enable_dev $dev3 # fail single devices -vgremove -ff $vg -vgcreate -c n $vg $dev1 $dev2 $dev3 +aux prepare_vg 5 +vgreduce $vg $dev4 lvcreate -m 1 --ig -L 1 -n mirror $vg - lvchange -a n $vg/mirror vgextend $vg $dev4 disable_dev $dev1 diff --git a/test/test-utils.sh b/test/test-utils.sh index fd804b15a..16638d125 100644 --- a/test/test-utils.sh +++ b/test/test-utils.sh @@ -373,6 +373,7 @@ prepare_lvmconf() { level = 9 file = "$TESTDIR/debug.log" overwrite = 1 + activation = 1 } backup { backup = 0 diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 86a0d7df5..0d6020642 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -15,6 +15,7 @@ #include "tools.h" #include "polldaemon.h" #include "lv_alloc.h" +#include "metadata.h" struct lvconvert_params { int snapshot; @@ -603,6 +604,12 @@ static struct dm_list *_failed_pv_list(struct volume_group *vg) return failed_pvs; } +static int _is_partial_lv(struct logical_volume *lv, + void *baton __attribute((unused))) +{ + return lv->status & PARTIAL_LV; +} + /* * Walk down the stacked mirror LV to the original mirror LV. */ @@ -727,7 +734,7 @@ static int _lv_update_log_type(struct cmd_context *cmd, } /* Reducing redundancy of the log */ - return remove_mirror_images(log_lv, log_count, operable_pvs, 1U); + return remove_mirror_images(log_lv, log_count, is_mirror_image_removable, operable_pvs, 1U); } /* @@ -913,6 +920,34 @@ static int _lvconvert_mirrors_parse_params(struct cmd_context *cmd, return 1; } +static int _reload_lv(struct cmd_context *cmd, struct logical_volume *lv) +{ + log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); + + if (!vg_write(lv->vg)) + return_0; + + if (!suspend_lv(cmd, lv)) { + log_error("Failed to lock %s", lv->name); + vg_revert(lv->vg); + return 0; + } + + if (!vg_commit(lv->vg)) { + if (!resume_lv(cmd, lv)) + stack; + return_0; + } + + log_very_verbose("Updating \"%s\" in kernel", lv->name); + + if (!resume_lv(cmd, lv)) { + log_error("Problem reactivating %s", lv->name); + return 0; + } + return 1; +} + /* * _lvconvert_mirrors_aux * @@ -1004,6 +1039,16 @@ static int _lvconvert_mirrors_aux(struct cmd_context *cmd, return 0; } + /* + * Is there already a convert in progress? We do not + * currently allow more than one. + */ + if (find_temporary_mirror(lv) || (lv->status & CONVERTING)) { + log_error("%s is already being converted. Unable to start another conversion.", + lv->name); + return 0; + } + /* * Log addition/removal should be done before the layer * insertion to make the end result consistent with @@ -1064,7 +1109,7 @@ static int _lvconvert_mirrors_aux(struct cmd_context *cmd, nmc, operable_pvs)) return 0; } else if (!lv_remove_mirrors(cmd, lv, nmc, nlc, - operable_pvs, 0)) + is_mirror_image_removable, operable_pvs, 0)) return_0; goto out; /* Just in case someone puts code between */ @@ -1084,29 +1129,8 @@ out: out_skip_log_convert: - log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name); - - if (!vg_write(lv->vg)) - return_0; - - if (!suspend_lv(cmd, lv)) { - log_error("Failed to lock %s", lv->name); - vg_revert(lv->vg); - goto out; - } - - if (!vg_commit(lv->vg)) { - if (!resume_lv(cmd, lv)) - stack; - goto_out; - } - - log_very_verbose("Updating \"%s\" in kernel", lv->name); - - if (!resume_lv(cmd, lv)) { - log_error("Problem reactivating %s", lv->name); - goto out; - } + if (!_reload_lv(cmd, lv)) + return 0; return 1; } @@ -1139,6 +1163,8 @@ static int _lvconvert_mirrors_repair(struct cmd_context *cmd, cmd->partial_activation = 1; lp->need_polling = 0; + lv_check_transient(lv); /* TODO check this in lib for all commands? */ + if (!(lv->status & PARTIAL_LV)) { log_error("%s is consistent. Nothing to repair.", lv->name); return 1; @@ -1195,11 +1221,13 @@ static int _lvconvert_mirrors_repair(struct cmd_context *cmd, if (!_lv_update_log_type(cmd, lp, lv, failed_pvs, new_log_count)) return 0; - /* - * Remove all failed_pvs - */ - if (!_lvconvert_mirrors_aux(cmd, lv, lp, failed_pvs, - lp->mirrors, new_log_count)) + if (failed_mirrors) { + if (!lv_remove_mirrors(cmd, lv, failed_mirrors, new_log_count, + _is_partial_lv, NULL, 0)) + return 0; + } + + if (!_reload_lv(cmd, lv)) return 0; /* @@ -1209,6 +1237,13 @@ static int _lvconvert_mirrors_repair(struct cmd_context *cmd, if (replace_mirrors) lp->mirrors = old_mimage_count; + /* + * It does not make sense to replace the log if the volume is no longer + * a mirror. + */ + if (!replace_mirrors && lp->mirrors == 1) + replace_log = 0; + log_count = replace_log ? old_log_count : new_log_count; while (replace_mirrors || replace_log) { @@ -1226,10 +1261,10 @@ static int _lvconvert_mirrors_repair(struct cmd_context *cmd, } } - if (lp->mirrors != old_mimage_count) + if (replace_mirrors && lp->mirrors != old_mimage_count) log_warn("WARNING: Failed to replace %d of %d images in volume %s", old_mimage_count - lp->mirrors, old_mimage_count, lv->name); - if (log_count != old_log_count) + if (replace_log && log_count != old_log_count) log_warn("WARNING: Failed to replace %d of %d logs in volume %s", old_log_count - log_count, old_log_count, lv->name); diff --git a/tools/pvmove.c b/tools/pvmove.c index f392de504..2a3c27c6e 100644 --- a/tools/pvmove.c +++ b/tools/pvmove.c @@ -292,7 +292,7 @@ static int _detach_pvmove_mirror(struct cmd_context *cmd, /* Update metadata to remove mirror segments and break dependencies */ dm_list_init(&lvs_completed); - if (!lv_remove_mirrors(cmd, lv_mirr, 1, 0, NULL, PVMOVE) || + if (!lv_remove_mirrors(cmd, lv_mirr, 1, 0, NULL, NULL, PVMOVE) || !remove_layers_for_segments_all(cmd, lv_mirr, PVMOVE, &lvs_completed)) { return 0;