diff options
Diffstat (limited to 'lib/metadata/lv_manip.c')
-rw-r--r-- | lib/metadata/lv_manip.c | 190 |
1 files changed, 139 insertions, 51 deletions
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index 43e09a447..1c7a1643a 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -4046,6 +4046,25 @@ bad: return 0; } +/* Add all rmeta SubLVs for @seg to @lvs and return allocated @lvl to free by caller. */ +static struct lv_list *_raid_list_metalvs(struct lv_segment *seg, struct dm_list *lvs) +{ + uint32_t s; + struct lv_list *lvl; + + dm_list_init(lvs); + + if (!(lvl = dm_pool_alloc(seg->lv->vg->vgmem, sizeof(*lvl) * seg->area_count))) + return_NULL; + + for (s = 0; s < seg->area_count; s++) { + lvl[s].lv = seg_metalv(seg, s); + dm_list_add(lvs, &lvl[s].list); + } + + return lvl; +} + static int _lv_extend_layered_lv(struct alloc_handle *ah, struct logical_volume *lv, uint32_t extents, uint32_t first_area, @@ -4057,7 +4076,6 @@ static int _lv_extend_layered_lv(struct alloc_handle *ah, uint32_t fa, s; int clear_metadata = 0; uint32_t area_multiple = 1; - int fail; if (!(segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_STRIPED))) return_0; @@ -4135,74 +4153,50 @@ static int _lv_extend_layered_lv(struct alloc_handle *ah, return_0; if (clear_metadata) { + struct volume_group *vg = lv->vg; + /* * We must clear the metadata areas upon creation. */ - /* FIXME VG is not in a fully-consistent state here and should not be committed! */ - if (!vg_write(lv->vg) || !vg_commit(lv->vg)) - return_0; - - if (test_mode()) - log_verbose("Test mode: Skipping wiping of metadata areas."); - else { - fail = 0; - /* Activate all rmeta devices locally first (more efficient) */ - for (s = 0; !fail && s < seg->area_count; s++) { - meta_lv = seg_metalv(seg, s); - - if (!activate_lv(meta_lv->vg->cmd, meta_lv)) { - log_error("Failed to activate %s for clearing.", - display_lvname(meta_lv)); - fail = 1; - } - } - - /* Clear all rmeta devices */ - for (s = 0; !fail && s < seg->area_count; s++) { - meta_lv = seg_metalv(seg, s); - log_verbose("Clearing metadata area of %s.", - display_lvname(meta_lv)); - /* - * Rather than wiping meta_lv->size, we can simply - * wipe '1' to remove the superblock of any previous - * RAID devices. It is much quicker. - */ - if (!wipe_lv(meta_lv, (struct wipe_params) - { .do_zero = 1, .zero_sectors = 1 })) { - stack; - fail = 1; - } - } - - /* Deactivate all rmeta devices */ - for (s = 0; s < seg->area_count; s++) { - meta_lv = seg_metalv(seg, s); + /* + * Declare the new RaidLV as temporary to avoid visible SubLV + * failures on activation until after we wiped them so that + * we can avoid activating crashed, potentially partially + * wiped RaidLVs. + */ + lv->status |= LV_TEMPORARY; - if (!deactivate_lv(meta_lv->vg->cmd, meta_lv)) { - log_error("Failed to deactivate %s after clearing.", - display_lvname(meta_lv)); - fail = 1; - } + if (test_mode()) { + /* FIXME VG is not in a fully-consistent state here and should not be committed! */ + if (!vg_write(vg) || !vg_commit(vg)) + return_0; - /* Wipe any temporary tags required for activation. */ - str_list_wipe(&meta_lv->tags); - } + log_verbose("Test mode: Skipping wiping of metadata areas."); + } else { + struct dm_list meta_lvs; + struct lv_list *lvl; - if (fail) { - /* Fail, after trying to deactivate all we could */ - struct volume_group *vg = lv->vg; + if (!(lvl = _raid_list_metalvs(seg, &meta_lvs))) + return 0; + /* Wipe lv list committing metadata */ + if (!activate_and_wipe_lvlist(&meta_lvs, 1)) { + /* If we failed clearing rmeta SubLVs, try removing the new RaidLV */ if (!lv_remove(lv)) log_error("Failed to remove LV"); else if (!vg_write(vg) || !vg_commit(vg)) log_error("Failed to commit VG %s", vg->name); return_0; } + + dm_pool_free(vg->vgmem, lvl); } for (s = 0; s < seg->area_count; s++) lv_set_hidden(seg_metalv(seg, s)); + + lv->status &= ~LV_TEMPORARY; } return 1; @@ -7298,6 +7292,100 @@ out: return 1; } +/* + * Optionally makes on-disk metadata changes if @commit + * + * If LV is active: + * wipe any signatures and clear first sector of LVs listed on @lv_list + * otherwise: + * activate, wipe (as above), deactivate + * + * Returns: 1 on success, 0 on failure + */ +int activate_and_wipe_lvlist(struct dm_list *lv_list, int commit) +{ + struct lv_list *lvl; + struct volume_group *vg = NULL; + unsigned i = 0, sz = dm_list_size(lv_list); + char *was_active; + int r = 1; + + if (!sz) { + log_debug_metadata(INTERNAL_ERROR "Empty list of LVs given for wiping."); + return 1; + } + + dm_list_iterate_items(lvl, lv_list) { + if (!lv_is_visible(lvl->lv)) { + log_error(INTERNAL_ERROR + "LVs must be set visible before wiping."); + return 0; + } + vg = lvl->lv->vg; + } + + if (test_mode()) + return 1; + + /* + * FIXME: only vg_[write|commit] if LVs are not already written + * as visible in the LVM metadata (which is never the case yet). + */ + if (commit && + (!vg || !vg_write(vg) || !vg_commit(vg))) + return_0; + + was_active = alloca(sz); + + dm_list_iterate_items(lvl, lv_list) + if (!(was_active[i++] = lv_is_active(lvl->lv))) { + lvl->lv->status |= LV_TEMPORARY; + if (!activate_lv(vg->cmd, lvl->lv)) { + log_error("Failed to activate localy %s for wiping.", + display_lvname(lvl->lv)); + r = 0; + goto out; + } + lvl->lv->status &= ~LV_TEMPORARY; + } + + dm_list_iterate_items(lvl, lv_list) { + log_verbose("Wiping metadata area %s.", display_lvname(lvl->lv)); + /* Wipe any know signatures */ + if (!wipe_lv(lvl->lv, (struct wipe_params) { .do_wipe_signatures = 1, .do_zero = 1, .zero_sectors = 1 })) { + log_error("Failed to wipe %s.", display_lvname(lvl->lv)); + r = 0; + goto out; + } + } +out: + /* TODO: deactivation is only needed with clustered locking + * in normal case we should keep device active + */ + sz = 0; + dm_list_iterate_items(lvl, lv_list) + if ((i > sz) && !was_active[sz++] && + !deactivate_lv(vg->cmd, lvl->lv)) { + log_error("Failed to deactivate %s.", display_lvname(lvl->lv)); + r = 0; /* Continue deactivating as many as possible. */ + } + + return r; +} + +/* Wipe logical volume @lv, optionally with @commit of metadata */ +int activate_and_wipe_lv(struct logical_volume *lv, int commit) +{ + struct dm_list lv_list; + struct lv_list lvl; + + lvl.lv = lv; + dm_list_init(&lv_list); + dm_list_add(&lv_list, &lvl.list); + + return activate_and_wipe_lvlist(&lv_list, commit); +} + static struct logical_volume *_create_virtual_origin(struct cmd_context *cmd, struct volume_group *vg, const char *lv_name, |