summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Rajnoha <prajnoha@redhat.com>2015-05-06 14:50:02 +0200
committerPeter Rajnoha <prajnoha@redhat.com>2015-05-08 09:57:08 +0200
commit6bec9810066d67a44944ca176662cbd9bcfc8518 (patch)
tree00ac3cda698e714187f7074342f592ba65797365
parentb0bdde977ab98d9f7f53fd4a99b92923c61a7251 (diff)
downloadlvm2-6bec9810066d67a44944ca176662cbd9bcfc8518.tar.gz
thin: add supporting infrastructure for tracking snapshot chain even if there's a gap
Thin snapshots can be chained. If we remove LVs in the middle of the chain, we would still like to know what was the original chain and hence we'd like to know from which thin LVs an LV descended. Normally we track only direct origin through seg->origin pointer (snapshot -> origin path) and through lv->segs_using_this_lv for the other way round (origin -> snapshots path). This patch add seg->indirect_origin and lv->indirect_segs_using_this_lv to track the dependency even if the chain is broken somewhere in the middle. Actual implementation of the removal and proper setting of the new fields and structures will be subject to subsequent patches - this patch adds only the infrastructure for making this extended dependency tracking possible. For example, let's consider this thin snapshot chain: LV1 -- LV2 -- LV3 -- LV4 After removing LV 2, we end up with: LV1 LV3 -- LV4 This patch adds support for recording the gap that resulted from removing the LV in thin snapshot chain (removed LVs are prefixed with '!' here): ____________ / \ LV1 !LV2 LV3 -- LV4 The !LV2 is the removed LV - recorded in lv->removed_ancestors list (LV3 holds this list in this situation). The LV3 also records LV1 as its indirect origin. After removing LV3, we end up with: _____________________ / \ LV1 !LV2 !LV3 LV4 In terms of the new fields/structures, we end up with: LV4->removed_ancestor_lv_names = [ "LV2", "LV3" ] first_seg(LV4)->origin = NULL first_seg(LV4)->indirect_origin = LV1 LV1->indirect_segs_using_this_lv = [ first_seg(LV4) ] ==== A little bit more complex example: LV1 -- LV2 -- LV3 -- LV4 \ --------------- LV5 -- LV6 Then after removing the LV2, we end up with: _____________________ /__________ \ // \ \ LV1 !LV2 LV3 -- LV4 \ \ LV5 -- LV6 Then after removing the LV5, we end up with: ____________________________ /__________ \ // \ \ LV1 !LV2 LV3 -- LV4 \ \ !LV5 LV6 LV3->removed_ancestor_lv_names = [ "LV2" ] first_seg(LV3)->origin = NULL first_seg(LV3)->indirect_origin = LV1 LV6->removed_ancestor_lv_names = [ "LV5" ] first_seg(LV6)->origin = NULL first_seg(LV6)->indirect_origin = LV1 LV1->indirect_segs_using_this_lv = [ first_seg(LV3), first_seg(LV6) ]
-rw-r--r--lib/format_text/export.c3
-rw-r--r--lib/format_text/import_vsn1.c7
-rw-r--r--lib/metadata/lv.h3
-rw-r--r--lib/metadata/lv_manip.c53
-rw-r--r--lib/metadata/metadata-exported.h4
-rw-r--r--lib/metadata/metadata.h2
-rw-r--r--lib/metadata/thin_manip.c29
-rw-r--r--lib/thin/thin.c16
8 files changed, 116 insertions, 1 deletions
diff --git a/lib/format_text/export.c b/lib/format_text/export.c
index 11766ac48..652e89658 100644
--- a/lib/format_text/export.c
+++ b/lib/format_text/export.c
@@ -686,6 +686,9 @@ static int _print_lv(struct formatter *f, struct logical_volume *lv)
if (!_out_list(f, &lv->tags, "tags"))
return_0;
+ if (!_out_list(f, &lv->removed_ancestor_lv_names, "removed_ancestor_lv_names"))
+ return_0;
+
if (lv->timestamp) {
ts = (time_t)lv->timestamp;
strncpy(buffer, "# ", sizeof(buffer));
diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c
index 96d10e456..f432fa8cc 100644
--- a/lib/format_text/import_vsn1.c
+++ b/lib/format_text/import_vsn1.c
@@ -638,6 +638,13 @@ static int _read_lvnames(struct format_instance *fid __attribute__((unused)),
return 0;
}
+ if (dm_config_get_list(lvn, "removed_ancestor_lv_names", &cv) &&
+ !(_read_str_list(mem, &lv->removed_ancestor_lv_names, cv))) {
+ log_error("Couldn't read removed ancestor names for logical "
+ "volume %s/%s.", vg->name, lv->name);
+ return 0;
+ }
+
if (!dm_hash_insert(lv_hash, lv->name, lv))
return_0;
diff --git a/lib/metadata/lv.h b/lib/metadata/lv.h
index 44750851a..4dad1ed53 100644
--- a/lib/metadata/lv.h
+++ b/lib/metadata/lv.h
@@ -50,6 +50,9 @@ struct logical_volume {
struct dm_list tags;
struct dm_list segs_using_this_lv;
+ struct dm_list indirect_segs_using_this_lv;
+ struct dm_list removed_ancestor_lv_names;
+
uint64_t timestamp;
const char *hostname;
};
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 85f727f76..e24b80c3c 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -837,6 +837,57 @@ int remove_seg_from_segs_using_this_lv(struct logical_volume *lv,
return 0;
}
+int add_seg_to_indirect_segs_using_this_lv(struct logical_volume *lv,
+ struct lv_segment *seg)
+{
+ struct seg_list *sl;
+
+ dm_list_iterate_items(sl, &lv->indirect_segs_using_this_lv) {
+ if (sl->seg == seg) {
+ sl->count++;
+ return 1;
+ }
+ }
+
+ log_very_verbose("Adding %s:%" PRIu32 "as an indirect user of %s",
+ seg->lv->name, seg->le, lv->name);
+
+ if (!(sl = dm_pool_zalloc(lv->vg->vgmem, sizeof(*sl)))) {
+ log_error("Failed to allocate segment list");
+ return 0;
+ }
+
+ sl->count = 1;
+ sl->seg = seg;
+ dm_list_add(&lv->indirect_segs_using_this_lv, &sl->list);
+
+ return 1;
+}
+
+int remove_seg_from_indirect_segs_using_this_lv(struct logical_volume *lv,
+ struct lv_segment *seg)
+{
+ struct seg_list *sl;
+
+ dm_list_iterate_items(sl, &lv->indirect_segs_using_this_lv) {
+ if (sl->seg != seg)
+ continue;
+ if (sl->count > 1)
+ sl->count--;
+ else {
+ log_very_verbose("%s:%" PRIu32 " is no longer an "
+ "indirect user of %s", seg->lv->name,
+ seg->le, lv->name);
+ dm_list_del(&sl->list);
+ }
+ return 1;
+ }
+
+ log_error(INTERNAL_ERROR "Segment %s:%u is not an indirect user of %s.",
+ seg->lv->name, seg->le, lv->name);
+ return 0;
+}
+
/*
* This is a function specialized for the common case where there is
* only one segment which uses the LV.
@@ -5342,6 +5393,8 @@ struct logical_volume *alloc_lv(struct dm_pool *mem)
dm_list_init(&lv->segments);
dm_list_init(&lv->tags);
dm_list_init(&lv->segs_using_this_lv);
+ dm_list_init(&lv->indirect_segs_using_this_lv);
+ dm_list_init(&lv->removed_ancestor_lv_names);
dm_list_init(&lv->rsites);
return lv;
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 47fb9ae56..3d94ba6ce 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -430,6 +430,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 *indirect_origin;
struct logical_volume *merge_lv; /* thin, merge descendent lv into this ancestor */
struct logical_volume *cow;
struct dm_list origin_list;
@@ -803,6 +804,9 @@ int vg_remove_pool_metadata_spare(struct volume_group *vg);
int attach_thin_external_origin(struct lv_segment *seg,
struct logical_volume *external_lv);
int detach_thin_external_origin(struct lv_segment *seg);
+int attach_thin_indirect_origin(struct lv_segment *seg,
+ struct logical_volume *indirect_origin);
+int detach_thin_indirect_origin(struct lv_segment *seg);
int attach_pool_metadata_lv(struct lv_segment *pool_seg,
struct logical_volume *pool_metadata_lv);
int detach_pool_metadata_lv(struct lv_segment *pool_seg,
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
index 031d19e5f..f6bb49b53 100644
--- a/lib/metadata/metadata.h
+++ b/lib/metadata/metadata.h
@@ -434,6 +434,8 @@ int lv_split_segment(struct logical_volume *lv, uint32_t le);
*/
int add_seg_to_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
int remove_seg_from_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
+int add_seg_to_indirect_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
+int remove_seg_from_indirect_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
int for_each_sub_lv_except_pools(struct logical_volume *lv,
int (*fn)(struct logical_volume *lv, void *data),
diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c
index e617b3c58..fb5fa0012 100644
--- a/lib/metadata/thin_manip.c
+++ b/lib/metadata/thin_manip.c
@@ -121,6 +121,35 @@ int detach_thin_external_origin(struct lv_segment *seg)
return 1;
}
+int attach_thin_indirect_origin(struct lv_segment *seg,
+ struct logical_volume *indirect_origin)
+{
+ if (seg->indirect_origin) {
+ log_error(INTERNAL_ERROR "LV \"%s\" already has indirect origin.",
+ seg->lv->name);
+ return 0;
+ }
+
+ seg->indirect_origin = indirect_origin;
+
+ if (indirect_origin && !add_seg_to_indirect_segs_using_this_lv(indirect_origin, seg))
+ return_0;
+
+ return 1;
+}
+
+int detach_thin_indirect_origin(struct lv_segment *seg)
+{
+ if (seg->indirect_origin) {
+ if (!remove_seg_from_indirect_segs_using_this_lv(seg->indirect_origin, seg))
+ return_0;
+
+ seg->indirect_origin = NULL;
+ }
+
+ return 1;
+}
+
int lv_is_merging_thin_snapshot(const struct logical_volume *lv)
{
struct lv_segment *seg = first_seg(lv);
diff --git a/lib/thin/thin.c b/lib/thin/thin.c
index e2506034a..c5f806b50 100644
--- a/lib/thin/thin.c
+++ b/lib/thin/thin.c
@@ -463,7 +463,8 @@ 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, *merge_lv = NULL;
+ struct logical_volume *pool_lv, *origin = NULL, *indirect_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");
@@ -482,6 +483,14 @@ 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, "indirect_origin")) {
+ if (!dm_config_get_str(sn, "indirect_origin", &lv_name))
+ return SEG_LOG_ERROR("Indirect origin must be a string in");
+
+ if (!(indirect_origin = find_lv(seg->lv->vg, lv_name)))
+ return SEG_LOG_ERROR("Unknown indirect 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");
@@ -510,6 +519,9 @@ static int _thin_text_import(struct lv_segment *seg,
if (!attach_thin_external_origin(seg, external_lv))
return_0;
+ if (!attach_thin_indirect_origin(seg, indirect_origin))
+ return_0;
+
return 1;
}
@@ -519,6 +531,8 @@ static int _thin_text_export(const struct lv_segment *seg, struct formatter *f)
outf(f, "transaction_id = %" PRIu64, seg->transaction_id);
outf(f, "device_id = %d", seg->device_id);
+ if (seg->indirect_origin)
+ outf(f, "indirect_origin = \"%s\"", seg->indirect_origin->name);
if (seg->external_lv)
outf(f, "external_origin = \"%s\"", seg->external_lv->name);
if (seg->origin)