From e2619d9ec13ce230ad7d99ef3c20aa305e4e2347 Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Tue, 29 Nov 2005 18:20:23 +0000 Subject: [PATCH] more lvconvert mirror code --- WHATS_NEW | 5 + lib/metadata/metadata.h | 10 +- lib/metadata/mirror.c | 223 +++++++++++++++++++++++++++++++--------- tools/commands.h | 3 +- tools/lvconvert.c | 114 +++++++++++++++++--- tools/vgreduce.c | 2 +- 6 files changed, 293 insertions(+), 64 deletions(-) diff --git a/WHATS_NEW b/WHATS_NEW index aad71cc99..dcf9c80bf 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,10 @@ Version 2.02.02 ==================================== + Add some activation logic to remove_mirror_images(). + lvconvert can remove specified PVs from a mirror. + lvconvert turns an existing LV into a mirror. + Allow signed mirrors arguments. + Move create_mirror_log() into toollib. Determine parallel PVs to avoid with ALLOC_NORMAL allocation. Fix lv_empty. diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 0ffde9b1e..3e0a73c5b 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -561,8 +561,14 @@ int create_mirror_layers(struct alloc_handle *ah, uint32_t status, uint32_t region_size, struct logical_volume *log_lv); -int remove_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors); -int remove_all_mirror_images(struct logical_volume *lv); +int add_mirror_layers(struct alloc_handle *ah, + uint32_t num_mirrors, + uint32_t existing_mirrors, + struct logical_volume *lv, + struct segment_type *segtype); + +int remove_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors, + struct list *removable_pvs, int remove_log); /* * Given mirror image or mirror log segment, find corresponding mirror segment */ diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c index b6b5e4e21..eeecedc8a 100644 --- a/lib/metadata/mirror.c +++ b/lib/metadata/mirror.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -21,6 +21,7 @@ #include "activate.h" #include "lv_alloc.h" #include "lvm-string.h" +#include "locking.h" /* FIXME Should not be used in this file */ struct lv_segment *find_mirror_seg(struct lv_segment *seg) { @@ -62,6 +63,9 @@ static void _move_lv_segments(struct logical_volume *lv_to, struct logical_volum list_init(&lv_from->segments); + lv_to->le_count = lv_from->le_count; + lv_to->size = lv_from->size; + lv_from->le_count = 0; lv_from->size = 0; } @@ -69,65 +73,163 @@ static void _move_lv_segments(struct logical_volume *lv_to, struct logical_volum /* * Reduce mirrored_seg to num_mirrors images. */ -int remove_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors) +int remove_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors, + struct list *removable_pvs, int remove_log) { uint32_t m; + uint32_t s, s1; + struct logical_volume *sub_lv; + struct logical_volume *log_lv = NULL; + struct logical_volume *lv1 = NULL; + struct physical_volume *pv; + struct lv_segment *seg; + struct lv_segment_area area; + int all_pvs_removable, pv_found; + struct pv_list *pvl; + uint32_t old_area_count = mirrored_seg->area_count; + uint32_t new_area_count = mirrored_seg->area_count; + + log_very_verbose("Reducing mirror set from %" PRIu32 " to %" + PRIu32 " image(s)%s.", + old_area_count, num_mirrors, + remove_log ? " and no log volume" : ""); + + /* Move removable_pvs to end of array */ + if (removable_pvs) { + for (s = 0; s < mirrored_seg->area_count; s++) { + all_pvs_removable = 1; + sub_lv = seg_lv(mirrored_seg, s); + list_iterate_items(seg, &sub_lv->segments) { + for (s1 = 0; s1 < seg->area_count; s1++) { + if (seg_type(seg, s1) != AREA_PV) + /* FIXME Recurse for AREA_LV */ + continue; + + pv = seg_pv(seg, s1); + + pv_found = 0; + list_iterate_items(pvl, removable_pvs) { + if (pv->dev->dev == pvl->pv->dev->dev) { + pv_found = 1; + break; + } + } + if (!pv_found) { + all_pvs_removable = 0; + break; + } + } + if (!all_pvs_removable) + break; + } + if (all_pvs_removable) { + /* Swap segment to end */ + new_area_count--; + area = mirrored_seg->areas[new_area_count]; + mirrored_seg->areas[new_area_count] = mirrored_seg->areas[s]; + mirrored_seg->areas[s] = area; + } + /* Found enough matches? */ + if (new_area_count == num_mirrors) + break; + } + if (new_area_count == mirrored_seg->area_count) { + log_error("No mirror images found using specified PVs."); + return 0; + } + } for (m = num_mirrors; m < mirrored_seg->area_count; m++) { + seg_lv(mirrored_seg, m)->status &= ~MIRROR_IMAGE; + seg_lv(mirrored_seg, m)->status |= VISIBLE_LV; + } + + mirrored_seg->area_count = num_mirrors; + + /* If no more mirrors, remove mirror layer */ + if (num_mirrors == 1) { + lv1 = seg_lv(mirrored_seg, 0); + _move_lv_segments(mirrored_seg->lv, lv1); + mirrored_seg->lv->status &= ~MIRRORED; + remove_log = 1; + } + + if (remove_log) { + log_lv = mirrored_seg->log_lv; + mirrored_seg->log_lv = NULL; + } + + /* + * To successfully remove these unwanted LVs we need to + * remove the LVs from the mirror set, commit that metadata + * then deactivate and remove them fully. + */ + + /* FIXME lv1 has no segments here so shouldn't be written to disk! */ + + if (!vg_write(mirrored_seg->lv->vg)) { + log_error("intermediate VG write failed."); + return 0; + } + + if (!suspend_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) { + log_error("Failed to lock %s", mirrored_seg->lv->name); + vg_revert(mirrored_seg->lv->vg); + return 0; + } + + if (!vg_commit(mirrored_seg->lv->vg)) { + resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv); + return 0; + } + + log_very_verbose("Updating \"%s\" in kernel", mirrored_seg->lv->name); + + if (!resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) { + log_error("Problem reactivating %s", mirrored_seg->lv->name); + return 0; + } + + /* Delete the 'orphan' LVs */ + for (m = num_mirrors; m < old_area_count; m++) { + if (!deactivate_lv(mirrored_seg->lv->vg->cmd, seg_lv(mirrored_seg, m))) { + stack; + return 0; + } + if (!lv_remove(seg_lv(mirrored_seg, m))) { stack; return 0; } } - mirrored_seg->area_count = num_mirrors; + if (lv1) { + if (!deactivate_lv(mirrored_seg->lv->vg->cmd, lv1)) { + stack; + return 0; + } + + if (!lv_remove(lv1)) { + stack; + return 0; + } + } + + if (log_lv) { + if (!deactivate_lv(mirrored_seg->lv->vg->cmd, log_lv)) { + stack; + return 0; + } + + if (!lv_remove(log_lv)) { + stack; + return 0; + } + } return 1; } -int remove_all_mirror_images(struct logical_volume *lv) -{ - struct lv_segment *seg; - struct logical_volume *lv1; - - seg = first_seg(lv); - - if (!remove_mirror_images(seg, 1)) { - stack; - return 0; - } - - if (seg->log_lv && !lv_remove(seg->log_lv)) { - stack; - return 0; - } - - lv1 = seg_lv(seg, 0); - - _move_lv_segments(lv, lv1); - - if (!lv_remove(lv1)) { - stack; - return 0; - } - - lv->status &= ~MIRRORED; - - return 1; -} - -/* - * Add mirror images to an existing mirror - */ -/* FIXME -int add_mirror_images(struct alloc_handle *ah, - uint32_t first_area, - uint32_t num_areas, - struct logical_volume *lv) -{ -} -*/ - static int _create_layers_for_mirror(struct alloc_handle *ah, uint32_t first_area, uint32_t num_mirrors, @@ -161,7 +263,10 @@ static int _create_layers_for_mirror(struct alloc_handle *ah, return 0; } - if (!lv_add_segment(ah, m, 1, img_lvs[m], + if (m < first_area) + continue; + + if (!lv_add_segment(ah, m - first_area, 1, img_lvs[m], get_segtype_from_string(lv->vg->cmd, "striped"), 0, NULL, 0, 0, 0, NULL)) { @@ -219,6 +324,30 @@ int create_mirror_layers(struct alloc_handle *ah, return 1; } +int add_mirror_layers(struct alloc_handle *ah, + uint32_t num_mirrors, + uint32_t existing_mirrors, + struct logical_volume *lv, + struct segment_type *segtype) +{ + struct logical_volume **img_lvs; + + if (!(img_lvs = alloca(sizeof(*img_lvs) * num_mirrors))) { + log_error("img_lvs allocation failed. " + "Remove new LV and retry."); + return 0; + } + + if (!_create_layers_for_mirror(ah, 0, num_mirrors, + lv, segtype, + img_lvs)) { + stack; + return 0; + } + + return lv_add_more_mirrored_areas(lv, img_lvs, num_mirrors, 0); +} + /* * Replace any LV segments on given PV with temporary mirror. * Returns list of LVs changed. diff --git a/tools/commands.h b/tools/commands.h index 1adfd724d..65af40613 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -85,11 +85,12 @@ xx(lvconvert, "\t[-d|--debug]\n" "\t[-h|-?|--help]\n" "\t[-m|--mirrors Mirrors]\n" + "\t[-R|--regionsize MirrorLogRegionSize]\n" "\t[-v|--verbose]\n" "\t[--version]" "\n" "\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n", - alloc_ARG, mirrors_ARG, test_ARG) + alloc_ARG, mirrors_ARG, regionsize_ARG, test_ARG) xx(lvcreate, "Create a logical volume", diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 5f11e185b..61ad73221 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -13,10 +13,13 @@ */ #include "tools.h" +#include "lv_alloc.h" struct lvconvert_params { const char *lv_name; uint32_t mirrors; + sign_t mirrors_sign; + uint32_t region_size; alloc_policy_t alloc; @@ -40,7 +43,29 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, return 0; } - lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0) + 1; + lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0); + lp->mirrors_sign = arg_sign_value(cmd, mirrors_ARG, 0); + + /* + * --regionsize is only valid when converting an LV into a mirror. + * This is checked when we know the state of the LV being converted. + */ + if (arg_count(cmd, regionsize_ARG)) { + if (arg_sign_value(cmd, regionsize_ARG, 0) == SIGN_MINUS) { + log_error("Negative regionsize is invalid"); + return 0; + } + lp->region_size = 2 * arg_uint_value(cmd, regionsize_ARG, 0); + } else + lp->region_size = 2 * find_config_int(cmd->cft->root, + "activation/mirror_region_size", + DEFAULT_MIRROR_REGION_SIZE); + + if (lp->region_size & (lp->region_size - 1)) { + log_error("Region size (%" PRIu32 ") must be a power of 2", + lp->region_size); + return 0; + } if (!argc) { log_error("Please give logical volume path"); @@ -61,8 +86,33 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l { struct lv_segment *seg; uint32_t existing_mirrors; - // struct alloc_handle *ah = NULL; - // struct logical_volume *log_lv; + struct alloc_handle *ah = NULL; + struct logical_volume *log_lv; + struct list *parallel_areas; + struct segment_type *segtype; + + seg = first_seg(lv); + existing_mirrors = seg->area_count; + + /* Adjust required number of mirrors */ + if (lp->mirrors_sign == SIGN_PLUS) + lp->mirrors = existing_mirrors + lp->mirrors; + else if (lp->mirrors_sign == SIGN_MINUS) { + if (lp->mirrors >= existing_mirrors) { + log_error("Logical volume %s only has %" PRIu32 " mirrors.", + lv->name, existing_mirrors); + return 0; + } + lp->mirrors = existing_mirrors - lp->mirrors; + } else + lp->mirrors += 1; + + if (arg_count(cmd, regionsize_ARG) && (lv->status & MIRRORED) && + (lp->region_size != seg->region_size)) { + log_error("Mirror log region size cannot be changed on " + "an existing mirror."); + return 0; + } if ((lp->mirrors == 1)) { if (!(lv->status & MIRRORED)) { @@ -70,8 +120,8 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l lv->name); return 1; } - /* FIXME If allocatable_pvs supplied only remove those */ - if (!remove_all_mirror_images(lv)) { + + if (!remove_mirror_images(seg, 1, lp->pv_count ? lp->pvh : NULL, 1)) { stack; return 0; } @@ -82,8 +132,6 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l "mirror segments.", lv->name); return 0; } - seg = first_seg(lv); - existing_mirrors = seg->area_count; if (lp->mirrors == existing_mirrors) { log_error("Logical volume %s already has %" PRIu32 " mirror(s).", lv->name, @@ -100,18 +148,58 @@ static int lvconvert_mirrors(struct cmd_context * cmd, struct logical_volume * l "supported yet."); return 0; } else { - if (!remove_mirror_images(seg, lp->mirrors)) { + /* Reduce number of mirrors */ + if (!remove_mirror_images(seg, lp->mirrors, lp->pv_count ? lp->pvh : NULL, 0)) { stack; return 0; } } } else { + /* Make existing LV into mirror set */ /* FIXME Share code with lvcreate */ - /* region size, log_name, create log_lv, zero it */ - // Allocate (mirrors) new areas & log - replace mirrored_pv with mirrored_lv - // Restructure as mirror - add existing param to create_mirror_layers - log_error("Adding mirror images is not supported yet."); - return 0; + + /* FIXME Why is this restriction here? Fix it! */ + list_iterate_items(seg, &lv->segments) { + if (seg_is_striped(seg) && seg->area_count > 1) { + log_error("Mirrors of striped volumes are not yet supported."); + return 0; + } + } + + if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv))) { + stack; + return 0; + } + + segtype = get_segtype_from_string(cmd, "striped"); + + if (!(ah = allocate_extents(lv->vg, NULL, segtype, 1, + lp->mirrors - 1, 1, + lv->le_count * (lp->mirrors - 1), + NULL, 0, 0, lp->pvh, + lp->alloc, + parallel_areas))) { + stack; + return 0; + } + + lp->region_size = adjusted_mirror_region_size(lv->vg->extent_size, + lv->le_count, + lp->region_size); + + if (!(log_lv = create_mirror_log(cmd, lv->vg, ah, + lp->alloc, + lv->name))) { + log_error("Failed to create mirror log."); + return 0; + } + + if (!create_mirror_layers(ah, 1, lp->mirrors, lv, + segtype, 0, lp->region_size, + log_lv)) { + stack; + return 0; + } } } diff --git a/tools/vgreduce.c b/tools/vgreduce.c index c75b195a5..cb0a0c5e4 100644 --- a/tools/vgreduce.c +++ b/tools/vgreduce.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. *