/*
* 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.
*
#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)
{
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;
}
/*
* 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;
-
- for (m = num_mirrors; m < mirrored_seg->area_count; m++) {
- if (!lv_remove(seg_lv(mirrored_seg, m))) {
- stack;
+ 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;
- return 1;
-}
+ /* 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;
+ }
-int remove_all_mirror_images(struct logical_volume *lv)
-{
- struct lv_segment *seg;
- struct logical_volume *lv1;
+ if (remove_log) {
+ log_lv = mirrored_seg->log_lv;
+ mirrored_seg->log_lv = NULL;
+ }
- seg = first_seg(lv);
+ /*
+ * 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.
+ */
- if (!remove_mirror_images(seg, 1)) {
- stack;
+ /* 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 (seg->log_lv && !lv_remove(seg->log_lv)) {
- stack;
+ 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;
}
- lv1 = seg_lv(seg, 0);
+ if (!vg_commit(mirrored_seg->lv->vg)) {
+ resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv);
+ return 0;
+ }
- _move_lv_segments(lv, lv1);
+ log_very_verbose("Updating \"%s\" in kernel", mirrored_seg->lv->name);
- if (!lv_remove(lv1)) {
- stack;
+ if (!resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+ log_error("Problem reactivating %s", mirrored_seg->lv->name);
return 0;
}
- lv->status &= ~MIRRORED;
+ /* 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;
+ }
- return 1;
-}
+ if (!lv_remove(seg_lv(mirrored_seg, m))) {
+ stack;
+ return 0;
+ }
+ }
-/*
- * 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)
-{
+ 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;
}
-*/
static int _create_layers_for_mirror(struct alloc_handle *ah,
uint32_t first_area,
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)) {
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.
*/
#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;
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");
{
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)) {
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;
}
"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,
"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;
+ }
}
}