diff options
author | Peter Rajnoha <prajnoha@redhat.com> | 2015-08-10 12:23:01 +0200 |
---|---|---|
committer | Peter Rajnoha <prajnoha@redhat.com> | 2016-02-03 17:40:31 +0100 |
commit | f2757e402760b26a601746a877f2407adb9dedb5 (patch) | |
tree | 03fdaf3945593015a02406bd81fccd0e3076d0ef | |
parent | c5e1fb9fcc7f0ab84bd77aac22118558cc614e40 (diff) | |
download | lvm2-f2757e402760b26a601746a877f2407adb9dedb5.tar.gz |
metadata: create dead LVs when LVs are removed and interconnect with live LVs to track history
When an LV is being removed, we create an instance of
"struct dead_logical_volume" wrapped up in "struct generic_logical_volume".
All instances of "struct dead_logical_volume" are then recorded in "dead_lvs"
list which is part of "struct volume_group".
The "dead lv" is then interconnected with "live LVs" (the ones
which are not removed yet) like this:
"g" stands for "this logical_volume is wrapped with struct generic_logical_volume"
"seg" stands for "representative segment is chosen for this logical_volume"
* Simple example:
g(liveLV1) --(indirect_users)--> g(deadLV2) --(indirect_users)--> g(liveLV3)
g(liveLV1) <--(indirect_origin)-- g(deadLV2) <--(indirect_origin)-- seg(liveLV3)
* Example with more dead LVs:
g(liveLV1) --(indirect_users)--> g(deadLV2) --(indirect_users)--> g(deadLV3)--(indirect_users)--> g(liveLV4)
g(liveLV1) <--(indirect_origin)-- g(deadLV2) <--(indirect_origin)-- g(deadLV3) <--(indirect_origin)-- seg(liveLV4)
* Or with branches:
g(liveLV1) --(indirect_users)--> g(deadLV2A) --(indirect_users)--> g(deadLV3A) --(indirect_users)--> g(liveLV4A)
\
-> g(deadLV2B) --(indirect_users)--> g(deadLV3BA) --(indirect_users)--> g(liveLV4BA)
\
-> g(deadLV3BB) --(indirect_users)--> g(liveLV4BB)
g(liveLV1) <--(indirect_origin)-- g(deadLV2A) <--(indirect_origin)-- g(deadLV3A) <--(indirect_origin)-- seg(liveLV4A)
\
-(indirect_origin)-- g(deadLV2B) <--(indirect_origin)-- g(deadLV3BA) <--(indirect_origin)-- seg(liveLV4BA)
\
-(indirect_origin)-- g(deadLV3BB) <--(indirect_origin)-- seg(liveLV4BB)
* Or simple example when dead LV is not remembered in records (compare with first "Simple example"):
g(liveLV1) --(indirect_users)--> g(liveLV3)
g(liveLV1) <--(indirect_origin)-- seg(liveLV3)
-rw-r--r-- | lib/metadata/metadata.c | 11 | ||||
-rw-r--r-- | lib/metadata/pool_manip.c | 109 |
2 files changed, 120 insertions, 0 deletions
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index d8a7128d1..d8bad0c3e 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -32,6 +32,7 @@ #include "archiver.h" #include "defaults.h" #include "lvmlockd.h" +#include "time.h" #include <math.h> #include <sys/param.h> @@ -3050,7 +3051,9 @@ int vg_write(struct volume_group *vg) struct pv_to_create *pv_to_create, *pv_to_create_safe; struct metadata_area *mda; struct lv_list *lvl; + struct glv_list *glvl; int revert = 0, wrote = 0; + time_t current_timestamp = 0; dm_list_iterate_items(lvl, &vg->lvs) { if (lvl->lv->lock_args && !strcmp(lvl->lv->lock_args, "pending")) { @@ -3062,6 +3065,14 @@ int vg_write(struct volume_group *vg) } } + dm_list_iterate_items(glvl, &vg->dead_lvs) { + if (!glvl->glv->dead->timestamp_removed) { + if (!current_timestamp) + current_timestamp = time(NULL); + glvl->glv->dead->timestamp_removed = (uint64_t) current_timestamp; + } + } + if (!vg_validate(vg)) return_0; diff --git a/lib/metadata/pool_manip.c b/lib/metadata/pool_manip.c index cb12aaacd..1aef595c0 100644 --- a/lib/metadata/pool_manip.c +++ b/lib/metadata/pool_manip.c @@ -26,6 +26,7 @@ #include "dev-type.h" #include "display.h" #include "toolcontext.h" +#include <stddef.h> int attach_pool_metadata_lv(struct lv_segment *pool_seg, struct logical_volume *metadata_lv) @@ -123,8 +124,89 @@ int attach_pool_lv(struct lv_segment *seg, return 1; } +static struct glv_list *_init_dead_glvl(struct dm_pool *mem, struct lv_segment *seg) +{ + struct glv_list *glvl; + struct dead_logical_volume *dlv; + char buf[NAME_LEN]; + char *dname; + + if (!(glvl = dm_pool_zalloc(mem, sizeof(struct glv_list)))) + goto_bad; + + if (!(glvl->glv = dm_pool_zalloc(mem, sizeof(struct generic_logical_volume)))) + goto_bad; + + if (!(dlv = dm_pool_zalloc(mem, sizeof(struct dead_logical_volume)))) + goto_bad; + + if (!(dname = generate_dead_lv_name(seg->lv->vg, "rlvol%d", buf, sizeof(buf)))) + goto_bad; + + if (!(dlv->dname = dm_pool_strdup(seg->lv->vg->vgmem, dname))) + goto_bad; + + dlv->lvid = seg->lv->lvid; + dlv->name = seg->lv->name; + dlv->vg = seg->lv->vg; + dlv->timestamp = seg->lv->timestamp; + dm_list_init(&dlv->indirect_users); + + glvl->glv->is_dead = 1; + glvl->glv->dead = dlv; + + return glvl; +bad: + log_error("Initialization of dead LV representation for removed logical " + "volume %s/%s failed.", seg->lv->vg->name, seg->lv->name); + if (glvl) + dm_pool_free(mem, glvl); + return NULL; +} + +static struct generic_logical_volume *_create_dead_glv(struct lv_segment *seg_to_remove) +{ + struct dm_pool *mem = seg_to_remove->lv->vg->vgmem; + struct generic_logical_volume *dead_glv, *origin_glv = NULL; + struct glv_list *dead_glvl; + int origin_glv_created = 0; + + if (!(dead_glvl = _init_dead_glvl(mem, seg_to_remove))) + goto_bad; + dead_glv = dead_glvl->glv; + + if (seg_to_remove->origin) { + if (!(origin_glv = get_or_create_glv(mem, seg_to_remove->origin, &origin_glv_created))) + goto_bad; + + if (!add_glv_to_indirect_user_list(mem, origin_glv, dead_glv)) + goto_bad; + } else if (seg_to_remove->indirect_origin) { + origin_glv = seg_to_remove->indirect_origin; + + if (!remove_glv_from_indirect_user_list(origin_glv, seg_to_remove->lv->this_glv)) + goto_bad; + + if (!add_glv_to_indirect_user_list(mem, origin_glv, dead_glv)) + goto_bad; + } + + dm_list_add(&seg_to_remove->lv->vg->dead_lvs, &dead_glvl->list); + return dead_glvl->glv; +bad: + log_error("Failed to create dead LV representation for removed logical " + "volume %s/%s.", seg_to_remove->lv->vg->name, seg_to_remove->lv->name); + if (origin_glv_created) + seg_to_remove->origin->this_glv = NULL; + if (dead_glvl) + dm_pool_free(mem, dead_glvl); + return NULL; +} + int detach_pool_lv(struct lv_segment *seg) { + struct generic_logical_volume *previous_glv = NULL, *glv, *user_glv; + struct glv_list *user_glvl, *tglvl; struct lv_thin_message *tmsg, *tmp; struct seg_list *sl, *tsl; int no_update = 0; @@ -177,6 +259,9 @@ int detach_pool_lv(struct lv_segment *seg) } } + if (!(previous_glv = _create_dead_glv(seg))) + return_0; + if (!detach_thin_external_origin(seg)) return_0; @@ -202,15 +287,39 @@ int detach_pool_lv(struct lv_segment *seg) (seg->lv != sl->seg->origin)) continue; + if (previous_glv) { + if (!(user_glv = get_or_create_glv(seg->lv->vg->vgmem, sl->seg->lv, NULL))) + return_0; + + if (!add_glv_to_indirect_user_list(seg->lv->vg->vgmem, previous_glv, user_glv)) + return_0; + } + if (!remove_seg_from_segs_using_this_lv(seg->lv, sl->seg)) return_0; /* Thin snapshot is now regular thin volume */ sl->seg->origin = NULL; } + dm_list_iterate_items_safe(user_glvl, tglvl, &seg->lv->indirect_users) { + user_glv = user_glvl->glv; + + if (!(glv = get_or_create_glv(seg->lv->vg->vgmem, seg->lv, NULL))) + return_0; + + if (!remove_glv_from_indirect_user_list(glv, user_glv)) + return_0; + + if (previous_glv) { + if (!add_glv_to_indirect_user_list(seg->lv->vg->vgmem, previous_glv, user_glv)) + return_0; + } + } + seg->lv->status &= ~THIN_VOLUME; seg->pool_lv = NULL; seg->origin = NULL; + seg->indirect_origin = NULL; return 1; } |