lib/activate/dev_manager.c | 41 ++++-- lib/metadata/lv.c | 10 +- lib/metadata/lv_manip.c | 4 +- lib/metadata/merge.c | 13 ++ lib/metadata/metadata-exported.h | 7 + lib/metadata/metadata.h | 2 +- lib/metadata/snapshot_manip.c | 20 +++- lib/metadata/thin_manip.c | 46 ++++++- lib/thin/thin.c | 30 ++++- tools/lvconvert.c | 273 +++++++++++++++++++++++++++++++++---- tools/toollib.c | 8 +- 11 files changed, 392 insertions(+), 62 deletions(-) diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c index 74afaac..2a88b59 100644 --- a/lib/activate/dev_manager.c +++ b/lib/activate/dev_manager.c @@ -2298,22 +2298,39 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, /* FIXME Seek a simpler way to lay out the snapshot-merge tree. */ - if (lv_is_origin(lv) && lv_is_merging_origin(lv) && !layer) { + if (lv_is_merging_origin(lv) && !layer) { /* * Clear merge attributes if merge isn't currently possible: * either origin or merging snapshot are open - * - but use "snapshot-merge" if it is already in use - * - open_count is always retrieved (as of dm-ioctl 4.7.0) - * so just use the tree's existing nodes' info */ - /* An activating merging origin won't have a node in the tree yet */ - if (((dinfo = _cached_info(dm->mem, dtree, lv, NULL)) && - dinfo->open_count) || - ((dinfo = _cached_info(dm->mem, dtree, - find_merging_snapshot(lv)->cow, NULL)) && - dinfo->open_count)) { - /* FIXME Is there anything simpler to check for instead? */ - if (!lv_has_target_type(dm->mem, lv, NULL, "snapshot-merge")) + if (lv_is_origin(lv)) { + /* Old snapshot-merge use-case: + * - use "snapshot-merge" if it is already in use + * - open_count is always retrieved (as of dm-ioctl 4.7.0) + * so just use the tree's existing nodes' info + */ + /* An activating merging origin won't have a node in the tree yet */ + if (((dinfo = _cached_info(dm->mem, dtree, lv, NULL)) && + dinfo->open_count) || + ((dinfo = _cached_info(dm->mem, dtree, + find_merging_snapshot(lv)->cow, NULL)) && + dinfo->open_count)) { + /* FIXME Is there anything simpler to check for instead? */ + if (!lv_has_target_type(dm->mem, lv, NULL, "snapshot-merge")) + laopts->no_merging = 1; + } + } else if (lv_is_thin_volume(lv)) { + /* FIXME: _cached_info() doesn't work, begs the question: + * does it work for old snapshot-merge (above)!? + */ + struct dm_info info; + struct logical_volume *snaplv = find_merging_snapshot(lv)->lv; + + if ((dev_manager_info(dm->mem, lv, NULL, 1, 0, &info, NULL) && + info.open_count) || + (dev_manager_info(dm->mem, snaplv, NULL, 1, 0, &info, NULL) && + info.open_count) || + (lv_is_active_locally(snaplv) && !deactivate_lv(dm->cmd, snaplv))) laopts->no_merging = 1; } } diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c index 0742562..7946bcc 100644 --- a/lib/metadata/lv.c +++ b/lib/metadata/lv.c @@ -553,8 +553,14 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv) repstr[0] = (lv->status & LV_NOTSYNCED) ? 'R' : 'r'; else if (lv->status & MIRRORED) repstr[0] = (lv->status & LV_NOTSYNCED) ? 'M' : 'm'; - else if (lv_is_thin_volume(lv)) - repstr[0] = 'V'; + else if (lv_is_thin_volume(lv)) { + if (lv_is_merging_origin(lv)) + repstr[0] = 'O'; + else if (lv_is_merging_thin_snap(lv)) + repstr[0] = 'S'; + else + repstr[0] = 'V'; + } else if (lv->status & VIRTUAL) repstr[0] = 'v'; else if (lv_is_thin_pool(lv)) diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index 9d67e71..b6ca7fc 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -409,14 +409,14 @@ struct lv_segment *alloc_lv_segment(const struct segment_type *segtype, /* If this is thin volume, thin snapshot is being created */ if (lv_is_thin_volume(thin_pool_lv)) { seg->transaction_id = first_seg(first_seg(thin_pool_lv)->pool_lv)->transaction_id; - if (!attach_pool_lv(seg, first_seg(thin_pool_lv)->pool_lv, thin_pool_lv)) + if (!attach_pool_lv(seg, first_seg(thin_pool_lv)->pool_lv, thin_pool_lv, NULL)) return_NULL; /* Use the same external origin */ if (!attach_thin_external_origin(seg, first_seg(thin_pool_lv)->external_lv)) return_NULL; } else if (lv_is_thin_pool(thin_pool_lv)) { seg->transaction_id = first_seg(thin_pool_lv)->transaction_id; - if (!attach_pool_lv(seg, thin_pool_lv, NULL)) + if (!attach_pool_lv(seg, thin_pool_lv, NULL, NULL)) return_NULL; } else { log_error(INTERNAL_ERROR "Volume %s is not thin volume or thin pool", diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c index 477d7bb..0bda427 100644 --- a/lib/metadata/merge.c +++ b/lib/metadata/merge.c @@ -248,6 +248,19 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg) lv->name, seg->external_lv->name); inc_error_count; } + + if (seg->merging_lv) { + if (!lv_is_thin_volume(seg->merging_lv)) { + log_error("LV %s: thin volume segment %u merging LV %s is not flagged as a thin LV", + lv->name, seg_count, seg->merging_lv->name); + inc_error_count; + } + if (!lv_is_merging_origin(seg->merging_lv)) { + log_error("LV %s: merging LV %s is not flagged as merging.", + lv->name, seg->merging_lv->name); + inc_error_count; + } + } } else { if (seg->pool_lv) { log_error("LV %s: segment %u must not have thin pool LV set", diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 412d29c..ecb2d0f 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -349,6 +349,7 @@ struct lv_segment { uint32_t chunk_size; /* For snapshots/thin_pool. In sectors. */ /* For thin_pool, 128..2097152. */ struct logical_volume *origin; /* snap and thin */ + struct logical_volume *merging_lv; /* thin, merge descendent lv into this ancestor */ struct logical_volume *cow; struct dm_list origin_list; uint32_t region_size; /* For mirrors, replicators - in sectors */ @@ -664,6 +665,7 @@ struct logical_volume *alloc_pool_metadata(struct logical_volume *pool_lv, uint32_t stripes, uint32_t stripe_size, uint64_t size, alloc_policy_t alloc, struct dm_list *pvh); +int get_thin_device_id(struct logical_volume *lv, uint32_t *device_id); /* * Activation options @@ -832,6 +834,11 @@ int vg_remove_snapshot(struct logical_volume *cow); int vg_check_status(const struct volume_group *vg, uint64_t status); +/* Given a thin snapshot LV, return its origin */ +struct logical_volume *origin_from_thin_snap(const struct logical_volume *lv); + +int lv_is_merging_thin_snap(const struct logical_volume *lv); + /* * Check if the VG reached maximal LVs count (if set) */ diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 364722f..78fb1b8 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -469,7 +469,7 @@ int detach_pool_metadata_lv(struct lv_segment *pool_seg, int attach_pool_data_lv(struct lv_segment *pool_seg, struct logical_volume *pool_data_lv); int attach_pool_lv(struct lv_segment *seg, struct logical_volume *pool_lv, - struct logical_volume *origin_lv); + struct logical_volume *origin_lv, struct logical_volume *merging_origin_lv); int detach_pool_lv(struct lv_segment *seg); int attach_pool_message(struct lv_segment *pool_seg, dm_thin_message_t type, struct logical_volume *lv, uint32_t delete_id, diff --git a/lib/metadata/snapshot_manip.c b/lib/metadata/snapshot_manip.c index f949b23..cf81192 100644 --- a/lib/metadata/snapshot_manip.c +++ b/lib/metadata/snapshot_manip.c @@ -28,6 +28,10 @@ int lv_is_origin(const struct logical_volume *lv) int lv_is_cow(const struct logical_volume *lv) { + /* must make sure a merging thin origin isn't confused as a cow LV */ + if (lv_is_merging_origin(lv) && lv_is_thin_volume(lv)) + return 0; + return (!lv_is_origin(lv) && lv->snapshot) ? 1 : 0; } @@ -155,6 +159,16 @@ void init_snapshot_seg(struct lv_segment *seg, struct logical_volume *origin, int init_snapshot_merge(struct lv_segment *snap_seg, struct logical_volume *origin) { + snap_seg->status |= MERGING; + origin->snapshot = snap_seg; + origin->status |= MERGING; + + /* partial re-use of snapshot merge setup for thinp */ + if (lv_is_thin_volume(origin)) { + snap_seg->merging_lv = origin; + return 1; + } + /* * Even though lv_is_visible(snap_seg->lv) returns 0, * the snap_seg->lv (name: snapshotX) is _not_ hidden; @@ -166,9 +180,6 @@ int init_snapshot_merge(struct lv_segment *snap_seg, * merge metadata (snap_seg->lv is now "internal") */ snap_seg->lv->status &= ~VISIBLE_LV; - snap_seg->status |= MERGING; - origin->snapshot = snap_seg; - origin->status |= MERGING; if (snap_seg->segtype->ops->target_present && !snap_seg->segtype->ops->target_present(snap_seg->lv->vg->cmd, @@ -180,6 +191,9 @@ int init_snapshot_merge(struct lv_segment *snap_seg, void clear_snapshot_merge(struct logical_volume *origin) { + if (lv_is_thin_volume(origin)) + origin->snapshot->merging_lv = NULL; + /* clear merge attributes */ origin->snapshot->status &= ~MERGING; origin->snapshot = NULL; diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c index f40fd74..26d6537 100644 --- a/lib/metadata/thin_manip.c +++ b/lib/metadata/thin_manip.c @@ -22,6 +22,23 @@ #include "defaults.h" #include "display.h" +/* + * thin snapshots don't have intermediate snapshot segments, so we + * need some extra thin-specific helpers to ease object classification. + */ +struct logical_volume *origin_from_thin_snap(const struct logical_volume *lv) +{ + struct lv_segment *seg = first_seg(lv); + if (seg->origin) + return seg->origin; + return NULL; +} + +int lv_is_merging_thin_snap(const struct logical_volume *lv) +{ + return (first_seg(lv)->status & MERGING) ? 1 : 0; +} + int attach_pool_metadata_lv(struct lv_segment *pool_seg, struct logical_volume *metadata_lv) { pool_seg->metadata_lv = metadata_lv; @@ -60,7 +77,7 @@ int attach_pool_data_lv(struct lv_segment *pool_seg, struct logical_volume *pool } int attach_pool_lv(struct lv_segment *seg, struct logical_volume *pool_lv, - struct logical_volume *origin) + struct logical_volume *origin, struct logical_volume *merging_lv) { seg->pool_lv = pool_lv; seg->lv->status |= THIN_VOLUME; @@ -69,7 +86,22 @@ int attach_pool_lv(struct lv_segment *seg, struct logical_volume *pool_lv, if (origin && !add_seg_to_segs_using_this_lv(origin, seg)) return_0; - return add_seg_to_segs_using_this_lv(pool_lv, seg); + if (!add_seg_to_segs_using_this_lv(pool_lv, seg)) + return_0; + + if (merging_lv) { + if (origin != merging_lv) { + if (!add_seg_to_segs_using_this_lv(merging_lv, seg)) + return_0; + } + + if (!init_snapshot_merge(seg, merging_lv)) { + log_error("Can't initialize snapshot merge."); + return 0; + } + } + + return 1; } int detach_pool_lv(struct lv_segment *seg) @@ -721,3 +753,13 @@ struct logical_volume *alloc_pool_metadata(struct logical_volume *pool_lv, return metadata_lv; } + +/* + * FIXME: implement proper generic per-target DM table parsing + */ +int get_thin_device_id(struct logical_volume *lv, uint32_t *device_id) +{ + *device_id = find_merging_snapshot(lv)->device_id; + + return 1; +} diff --git a/lib/thin/thin.c b/lib/thin/thin.c index 1867ef9..2b39b2a 100644 --- a/lib/thin/thin.c +++ b/lib/thin/thin.c @@ -423,7 +423,7 @@ static int _thin_text_import(struct lv_segment *seg, struct dm_hash_table *pv_hash __attribute__((unused))) { const char *lv_name; - struct logical_volume *pool_lv, *origin = NULL, *external_lv = NULL; + struct logical_volume *pool_lv, *origin = NULL, *external_lv = NULL, *merging_lv = NULL; if (!dm_config_get_str(sn, "thin_pool", &lv_name)) return SEG_LOG_ERROR("Thin pool must be a string in"); @@ -442,6 +442,13 @@ static int _thin_text_import(struct lv_segment *seg, return SEG_LOG_ERROR("Unknown origin %s in", lv_name); } + if (dm_config_has_node(sn, "merging_lv")) { + if (!dm_config_get_str(sn, "merging_lv", &lv_name)) + return SEG_LOG_ERROR("Merging lv must be a string in"); + if (!(merging_lv = find_lv(seg->lv->vg, lv_name))) + return SEG_LOG_ERROR("Unknown merging lv %s in", lv_name); + } + if (!dm_config_get_uint32(sn, "device_id", &seg->device_id)) return SEG_LOG_ERROR("Could not read device_id for"); @@ -457,7 +464,7 @@ static int _thin_text_import(struct lv_segment *seg, return SEG_LOG_ERROR("Unknown external origin %s in", lv_name); } - if (!attach_pool_lv(seg, pool_lv, origin)) + if (!attach_pool_lv(seg, pool_lv, origin, merging_lv)) return_0; if (!attach_thin_external_origin(seg, external_lv)) @@ -476,6 +483,8 @@ static int _thin_text_export(const struct lv_segment *seg, struct formatter *f) outf(f, "external_origin = \"%s\"", seg->external_lv->name); if (seg->origin) outf(f, "origin = \"%s\"", seg->origin->name); + if (seg->merging_lv) + outf(f, "merging_lv = \"%s\"", seg->merging_lv->name); return 1; } @@ -486,7 +495,7 @@ static int _thin_add_target_line(struct dev_manager *dm, struct cmd_context *cmd __attribute__((unused)), void **target_state __attribute__((unused)), struct lv_segment *seg, - const struct lv_activate_opts *laopts __attribute__((unused)), + const struct lv_activate_opts *laopts, struct dm_tree_node *node, uint64_t len, uint32_t *pvmove_mirror_count __attribute__((unused))) { @@ -504,6 +513,21 @@ static int _thin_add_target_line(struct dev_manager *dm, return 0; } + if (!laopts->no_merging) { + /* + * merge support for thinp snapshots is implemented by + * simply swapping the thinp device_id of the snapshot + * and origin. + */ + if (seg->merging_lv) { + /* snapshot, use merging_lv's device_id */ + device_id = first_seg(seg->merging_lv)->device_id; + } else if (lv_is_merging_origin(seg->lv)) { + /* origin, use merging snapshot's device_id */ + device_id = find_merging_snapshot(seg->lv)->device_id; + } + } + if (!dm_tree_node_add_thin_target(node, len, pool_dlid, device_id)) return_0; diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 3ea1987..6c0d4f7 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -669,6 +669,68 @@ static int _swap_lv_identifiers(struct cmd_context *cmd, return 1; } +static int _finish_lvconvert_thin_merge(struct cmd_context *cmd, + struct volume_group *vg, + struct logical_volume *lv, + struct dm_list *lvs_changed __attribute__((unused))) +{ + const char *origin_name = lv->name; + const char *snap_name = NULL; + struct lv_segment *snap_seg = NULL; + + snap_seg = find_merging_snapshot(lv); + if (!snap_seg) { + log_error("Logical volume %s has no merging snapshot.", lv->name); + return 0; + } + snap_name = snap_seg->lv->name; + + log_print_unless_silent("Merge of snapshot %s into logical volume %s has finished.", + snap_name, origin_name); + + if (!_swap_lv_identifiers(cmd, lv, snap_seg->lv)) { + log_error("Failed to swap origin %s with snapshot %s", + origin_name, snap_name); + return 0; + } + clear_snapshot_merge(lv); + if (!vg_write(lv->vg) || !vg_commit(lv->vg)) { + log_error("Could not write metadata after merging snapshot %s into %s.", + snap_name, origin_name); + return 0; + } + + return 1; +} + +static progress_t _poll_thin_merge_progress(struct cmd_context *cmd, + struct logical_volume *lv, + const char *name __attribute__((unused)), + struct daemon_parms *parms) +{ + uint32_t device_id; + + /* + * There is no need to poll more than once, a thin snapshot + * merge is immediate. So if the thin @lv isn't using the + * snapshot's device_id then the merge didn't happen. + */ + if (!get_thin_device_id(lv, &device_id)) { + log_error("%s: Failed query for thin device_id. Aborting merge.", lv->name); + return PROGRESS_CHECK_FAILED; + } + + /* + * If the @lv is using the merging snapshot's device_id + * then the merge was performed. + */ + if (device_id == find_merging_snapshot(lv)->device_id) + return PROGRESS_FINISHED_ALL; + + /* Merge would've been instant, don't keep polling */ + return PROGRESS_CHECK_FAILED; +} + static struct poll_functions _lvconvert_mirror_fns = { .get_copy_vg = _get_lvconvert_vg, .get_copy_lv = _get_lvconvert_lv, @@ -683,6 +745,13 @@ static struct poll_functions _lvconvert_merge_fns = { .finish_copy = _finish_lvconvert_merge, }; +static struct poll_functions _lvconvert_thin_merge_fns = { + .get_copy_vg = _get_lvconvert_vg, + .get_copy_lv = _get_lvconvert_lv, + .poll_progress = _poll_thin_merge_progress, + .finish_copy = _finish_lvconvert_thin_merge, +}; + int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv, unsigned background) { @@ -710,9 +779,17 @@ int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv, if (!lv_is_merging_origin(lv)) return poll_daemon(cmd, lv_full_name, uuid, background, 0, &_lvconvert_mirror_fns, "Converted"); - else + else { + struct poll_functions *merge_fns; + + if (lv_is_thin_volume(lv)) + merge_fns = &_lvconvert_thin_merge_fns; + else + merge_fns = &_lvconvert_merge_fns; + return poll_daemon(cmd, lv_full_name, uuid, background, 0, - &_lvconvert_merge_fns, "Merged"); + merge_fns, "Merged"); + } } static int _insert_lvconvert_layer(struct cmd_context *cmd, @@ -1796,9 +1873,9 @@ static int lvconvert_snapshot(struct cmd_context *cmd, return 1; } -static int lvconvert_merge(struct cmd_context *cmd, - struct logical_volume *lv, - struct lvconvert_params *lp) +static int lvconvert_merge_old_snapshot(struct cmd_context *cmd, + struct logical_volume *lv, + struct lvconvert_params *lp) { int r = 0; int merge_on_activate = 0; @@ -1888,6 +1965,92 @@ out: return r; } +static int lvconvert_merge_thin_snapshot(struct cmd_context *cmd, + struct logical_volume *lv, + struct lvconvert_params *lp) +{ + int r = 0; + + struct lvinfo info; + int origin_is_active, merge_on_activate = 0; + struct logical_volume *origin = origin_from_thin_snap(lv); + const char *origin_name = origin->name; + const char *snap_name = lv->name; + struct lv_segment *thin_snap_seg = first_seg(lv); + + // FIXME: allow origin to be specified + // FIXME: verify snapshot is descendant of specified origin + + /* Check if merge is possible */ + if (lv_is_merging_origin(origin)) { + log_error("Snapshot %s is already merging into the origin", + find_merging_snapshot(origin)->lv->name); + return 0; + } + + /* + * Prevent merge with open device(s) as it would likely lead + * to application/filesystem failure. Merge on origin's next + * activation if either the origin or snapshot LV are currently + * open. NOTE: using deactivate rather than suspend purely as + * a workaround for inability to _not_ suspend the entire tree + * (including the pool); which is too heavy for a merge operation. + */ + origin_is_active = lv_is_active_locally(origin); + if (origin_is_active) { + if (lv_info(cmd, origin, 0, &info, 1, 0) && + (!lv_check_not_in_use(cmd, origin, &info) || + !deactivate_lv(cmd, origin))) { + log_error("Can't merge over open origin volume"); + merge_on_activate = 1; + } + } + + if (lv_is_active_locally(lv) && + lv_info(cmd, lv, 0, &info, 1, 0) && + (!lv_check_not_in_use(cmd, lv, &info) || + !deactivate_lv(cmd, lv))) { + log_error("Can't merge when snapshot is open"); + merge_on_activate = 1; + } + + if (!init_snapshot_merge(thin_snap_seg, origin)) { + log_error("Can't initialize snapshot merge."); + return_0; + } + + /* store vg on disk(s) */ + if (!vg_write(lv->vg)) + return_0; + + if (merge_on_activate) { + /* commit vg but skip starting the merge */ + if (!vg_commit(lv->vg)) + return_0; + r = 1; + log_print_unless_silent("Merging of thin snapshot %s will occur " + "next activation.", snap_name); + goto out; + } + + if (!vg_commit(lv->vg)) + return_0; + + if (origin_is_active && !activate_lv(cmd, origin)) { + log_error("Failed to reactivate origin %s", origin_name); + goto out; + } + + lp->need_polling = 1; + lp->lv_to_poll = origin; + + r = 1; + log_print_unless_silent("Merging of volume %s started.", snap_name); +out: + backup(lv->vg); + return r; +} + static int _lvconvert_thinpool_external(struct cmd_context *cmd, struct logical_volume *pool_lv, struct logical_volume *external_lv, @@ -2238,13 +2401,80 @@ out: return r; } +static int _lvconvert_merge_old_snapshot(struct cmd_context *cmd, + struct logical_volume *lv, + struct lvconvert_params *lp) +{ + struct lvinfo info; + percent_t snap_percent; + + if (!lv_is_cow(lv)) { + log_error("\"%s\" is not a mergeable logical volume", + lv->name); + return 0; + } + if (lv_is_external_origin(origin_from_cow(lv))) { + log_error("Cannot merge snapshot \"%s\" into " + "the read-only external origin \"%s\".", + lv->name, origin_from_cow(lv)->name); + return 0; + } + if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) + && info.exists && info.live_table && + (!lv_snapshot_percent(lv, &snap_percent) || + snap_percent == PERCENT_INVALID)) { + log_error("Unable to merge invalidated snapshot LV \"%s\"", lv->name); + return 0; + } + if (!archive(lv->vg)) { + stack; + return 0; + } + + if (!lvconvert_merge_old_snapshot(cmd, lv, lp)) { + log_error("Unable to merge LV \"%s\" into its origin.", lv->name); + return 0; + } + + return 1; +} + +static int _lvconvert_merge_thin_snapshot(struct cmd_context *cmd, + struct logical_volume *lv, + struct lvconvert_params *lp) +{ + struct logical_volume *origin; + + origin = origin_from_thin_snap(lv); + if (!origin) { + log_error("\"%s\" is not a mergeable logical volume", + lv->name); + return 0; + } + if (lv_is_external_origin(origin)) { + log_error("Cannot merge snapshot \"%s\" into " + "the read-only external origin \"%s\".", + lv->name, origin_from_cow(lv)->name); + return 0; + } + if (!archive(lv->vg)) { + stack; + return 0; + } + + if (!lvconvert_merge_thin_snapshot(cmd, lv, lp)) { + log_error("Unable to merge LV \"%s\" into its origin.", lv->name); + return 0; + } + + return 1; +} + static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle) { struct lvconvert_params *lp = handle; struct dm_list *failed_pvs; - struct lvinfo info; - percent_t snap_percent; if (lv->status & LOCKED) { log_error("Cannot convert locked LV %s", lv->name); @@ -2274,30 +2504,13 @@ static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, lp->segtype = first_seg(lv)->segtype; if (lp->merge) { - if (!lv_is_cow(lv)) { - log_error("\"%s\" is not a mergeable logical volume", - lv->name); - return ECMD_FAILED; + if (lv_is_thin_volume(lv)) { + if (!_lvconvert_merge_thin_snapshot(cmd, lv, lp)) + return_ECMD_FAILED; } - if (lv_is_external_origin(origin_from_cow(lv))) { - log_error("Cannot merge snapshot \"%s\" into " - "the read-only external origin \"%s\".", - lv->name, origin_from_cow(lv)->name); - return ECMD_FAILED; - } - if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) - && info.exists && info.live_table && - (!lv_snapshot_percent(lv, &snap_percent) || - snap_percent == PERCENT_INVALID)) { - log_error("Unable to merge invalidated snapshot LV \"%s\"", lv->name); - return ECMD_FAILED; - } - if (!archive(lv->vg)) - return_ECMD_FAILED; - - if (!lvconvert_merge(cmd, lv, lp)) { - log_error("Unable to merge LV \"%s\" into its origin.", lv->name); - return ECMD_FAILED; + else { + if (!_lvconvert_merge_old_snapshot(cmd, lv, lp)) + return_ECMD_FAILED; } } else if (lp->snapshot) { if (lv->status & MIRRORED) { diff --git a/tools/toollib.c b/tools/toollib.c index 5db537f..5845db8 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -1328,14 +1328,8 @@ int lv_refresh(struct cmd_context *cmd, struct logical_volume *lv) /* * check if snapshot merge should be polled - * - unfortunately: even though the dev_manager will clear - * the lv's merge attributes if a merge is not possible; - * it is clearing a different instance of the lv (as - * retrieved with lv_from_lvid) - * - fortunately: polldaemon will immediately shutdown if the - * origin doesn't have a status with a snapshot percentage */ - if (background_polling() && lv_is_origin(lv) && lv_is_merging_origin(lv)) + if (background_polling() && lv_is_merging_origin(lv)) lv_spawn_background_polling(cmd, lv); out: