diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index 6846f4abe..2458447c0 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -491,14 +491,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 c22e9222f..93e4bfa32 100644 --- a/lib/metadata/merge.c +++ b/lib/metadata/merge.c @@ -258,6 +258,19 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg) lv->name, seg->external_lv->name); inc_error_count; } + + if (seg->merge_lv) { + if (!lv_is_thin_volume(seg->merge_lv)) { + log_error("LV %s: thin volume segment %u merging LV %s is not flagged as a thin LV", + lv->name, seg_count, seg->merge_lv->name); + inc_error_count; + } + if (!lv_is_merging_origin(seg->merge_lv)) { + log_error("LV %s: merging LV %s is not flagged as merging.", + lv->name, seg->merge_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 0186c6113..9798197a9 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -360,6 +360,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 *merge_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 */ @@ -881,7 +882,7 @@ int lv_is_visible(const struct logical_volume *lv); int pv_is_in_vg(struct volume_group *vg, struct physical_volume *pv); -/* Given a cow LV, return return the snapshot lv_segment that uses it */ +/* Given a cow or thin LV, return the snapshot lv_segment that uses it */ struct lv_segment *find_snapshot(const struct logical_volume *lv); /* Given a cow LV, return its origin */ diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 21ac204b2..0ab63f2d9 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -462,11 +462,12 @@ int fixup_imported_mirrors(struct volume_group *vg); * From thin_manip.c */ 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 *merge_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, int auto_increment); +int lv_is_merging_thin_snapshot(const struct logical_volume *lv); int pool_has_message(const struct lv_segment *seg, const struct logical_volume *lv, uint32_t device_id); int pool_below_threshold(const struct lv_segment *pool_seg); diff --git a/lib/metadata/snapshot_manip.c b/lib/metadata/snapshot_manip.c index e9a6d8726..e563a78d4 100644 --- a/lib/metadata/snapshot_manip.c +++ b/lib/metadata/snapshot_manip.c @@ -27,7 +27,8 @@ int lv_is_origin(const struct logical_volume *lv) int lv_is_cow(const struct logical_volume *lv) { - return (!lv_is_origin(lv) && lv->snapshot) ? 1 : 0; + /* Make sure a merging thin origin isn't confused as a cow LV */ + return (!lv_is_thin_volume(lv) && !lv_is_origin(lv) && lv->snapshot) ? 1 : 0; } static uint64_t _cow_max_size(uint64_t origin_size, uint32_t chunk_size) @@ -150,6 +151,13 @@ void init_snapshot_merge(struct lv_segment *snap_seg, origin->snapshot = snap_seg; origin->status |= MERGING; + if (lv_is_thin_volume(origin)) { + snap_seg->merge_lv = origin; + /* Making thin LV inivisible with regular log */ + lv_set_hidden(snap_seg->lv); + return; + } + /* * Even though lv_is_visible(snap_seg->lv) returns 0, * the snap_seg->lv (name: snapshotX) is _not_ hidden; @@ -166,6 +174,11 @@ void init_snapshot_merge(struct lv_segment *snap_seg, void clear_snapshot_merge(struct logical_volume *origin) { /* clear merge attributes */ + if (origin->snapshot->merge_lv) + /* Removed thin volume has to be visible */ + lv_set_visible(origin->snapshot->lv); + + origin->snapshot->merge_lv = NULL; origin->snapshot->status &= ~MERGING; origin->snapshot = NULL; origin->status &= ~MERGING; diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c index b3fedcf1a..0223752bc 100644 --- a/lib/metadata/thin_manip.c +++ b/lib/metadata/thin_manip.c @@ -60,7 +60,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 *merge_lv) { seg->pool_lv = pool_lv; seg->lv->status |= THIN_VOLUME; @@ -69,7 +69,19 @@ 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 (merge_lv) { + if (origin != merge_lv) { + if (!add_seg_to_segs_using_this_lv(merge_lv, seg)) + return_0; + } + + init_snapshot_merge(seg, merge_lv); + } + + return 1; } int detach_pool_lv(struct lv_segment *seg) @@ -247,6 +259,11 @@ int detach_thin_external_origin(struct lv_segment *seg) return 1; } +int lv_is_merging_thin_snapshot(const struct logical_volume *lv) +{ + return (first_seg(lv)->status & MERGING) ? 1 : 0; +} + /* * Check whether pool has some message queued for LV or for device_id * When LV is NULL and device_id is 0 it just checks for any message. diff --git a/lib/thin/thin.c b/lib/thin/thin.c index 8477aac93..5f0c27180 100644 --- a/lib/thin/thin.c +++ b/lib/thin/thin.c @@ -456,7 +456,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, *merge_lv = NULL; if (!dm_config_get_str(sn, "thin_pool", &lv_name)) return SEG_LOG_ERROR("Thin pool must be a string in"); @@ -475,6 +475,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, "merge")) { + if (!dm_config_get_str(sn, "merge", &lv_name)) + return SEG_LOG_ERROR("Merge lv must be a string in"); + if (!(merge_lv = find_lv(seg->lv->vg, lv_name))) + return SEG_LOG_ERROR("Unknown merge 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"); @@ -490,7 +497,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, merge_lv)) return_0; if (!attach_thin_external_origin(seg, external_lv)) @@ -509,6 +516,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->merge_lv) + outf(f, "merge = \"%s\"", seg->merge_lv->name); return 1; } @@ -519,7 +528,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))) { @@ -537,6 +546,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->merge_lv) { + /* snapshot, use merging lv's device_id */ + device_id = first_seg(seg->merge_lv)->device_id; + } else if (lv_is_merging_origin(seg->lv)) { + /* origin, use merging snapshot's device_id */ + device_id = find_snapshot(seg->lv)->device_id; + } + } + if (!dm_tree_node_add_thin_target(node, len, pool_dlid, device_id)) return_0;