1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-10-28 11:55:55 +03:00
lvm2/lib/metadata/mirror.c
Alasdair Kergon 15db9fcfbf Reinstate full PV size when removing from VG.
Support loopfiles for testing.
Complete the pv_segment support.
2005-05-03 17:28:23 +00:00

402 lines
9.9 KiB
C

/*
* Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License v.2.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lib.h"
#include "metadata.h"
#include "toolcontext.h"
#include "segtype.h"
#include "display.h"
#include "activate.h"
#include "lv_alloc.h"
/*
* Replace any LV segments on given PV with temporary mirror.
* Returns list of LVs changed.
*/
int insert_pvmove_mirrors(struct cmd_context *cmd,
struct logical_volume *lv_mirr,
struct list *source_pvl,
struct logical_volume *lv,
struct list *allocatable_pvs,
struct list *lvs_changed)
{
struct list *segh;
struct lv_segment *seg;
struct lv_list *lvl;
struct pv_list *pvl;
int lv_used = 0;
uint32_t s, start_le, extent_count = 0u;
struct segment_type *segtype;
struct pe_range *per;
uint32_t pe_start, pe_end, per_end, stripe_multiplier;
/* Only 1 PV may feature in source_pvl */
pvl = list_item(source_pvl->n, struct pv_list);
if (!(segtype = get_segtype_from_string(lv->vg->cmd, "mirror"))) {
stack;
return 0;
}
if (activation() && segtype->ops->target_present &&
!segtype->ops->target_present()) {
log_error("%s: Required device-mapper target(s) not "
"detected in your kernel", segtype->name);
return 0;
}
/* Split LV segments to match PE ranges */
list_iterate(segh, &lv->segments) {
seg = list_item(segh, struct lv_segment);
for (s = 0; s < seg->area_count; s++) {
if (seg->area[s].type != AREA_PV ||
seg->area[s].u.pv.pvseg->pv->dev != pvl->pv->dev)
continue;
/* Do these PEs need moving? */
list_iterate_items(per, pvl->pe_ranges) {
pe_start = seg->area[s].u.pv.pvseg->pe;
pe_end = pe_start + seg->area_len - 1;
per_end = per->start + per->count - 1;
/* No overlap? */
if ((pe_end < per->start) ||
(pe_start > per_end))
continue;
if (seg->segtype->flags & SEG_AREAS_STRIPED)
stripe_multiplier = seg->area_count;
else
stripe_multiplier = 1;
if ((per->start != pe_start &&
per->start > pe_start) &&
!lv_split_segment(lv, seg->le +
(per->start - pe_start) *
stripe_multiplier)) {
stack;
return 0;
}
if ((per_end != pe_end &&
per_end < pe_end) &&
!lv_split_segment(lv, seg->le +
(per_end - pe_start + 1) *
stripe_multiplier)) {
stack;
return 0;
}
}
}
}
/* Work through all segments on the supplied PV */
list_iterate(segh, &lv->segments) {
seg = list_item(segh, struct lv_segment);
for (s = 0; s < seg->area_count; s++) {
if (seg->area[s].type != AREA_PV ||
seg->area[s].u.pv.pvseg->pv->dev != pvl->pv->dev)
continue;
pe_start = seg->area[s].u.pv.pvseg->pe;
/* Do these PEs need moving? */
list_iterate_items(per, pvl->pe_ranges) {
per_end = per->start + per->count - 1;
if ((pe_start < per->start) ||
(pe_start > per_end))
continue;
log_debug("Matched PE range %u-%u against "
"%s %u len %u", per->start, per_end,
dev_name(seg->area[s].u.pv.pvseg->
pv->dev),
seg->area[s].u.pv.pvseg->pe,
seg->area_len);
/* First time, add LV to list of LVs affected */
if (!lv_used) {
if (!(lvl = pool_alloc(cmd->mem, sizeof(*lvl)))) {
log_error("lv_list alloc failed");
return 0;
}
lvl->lv = lv;
list_add(lvs_changed, &lvl->list);
lv_used = 1;
}
log_very_verbose("Moving %s:%u-%u of %s/%s",
dev_name(pvl->pv->dev),
seg->area[s].u.pv.pvseg->pe,
seg->area[s].u.pv.pvseg->pe +
seg->area_len - 1,
lv->vg->name, lv->name);
start_le = lv_mirr->le_count;
if (!lv_extend(lv->vg->fid, lv_mirr, segtype, 1,
seg->area_len, 0u, seg->area_len,
seg->area[s].u.pv.pvseg->pv,
seg->area[s].u.pv.pvseg->pe,
PVMOVE, allocatable_pvs,
lv->alloc)) {
log_error("Unable to allocate "
"temporary LV for pvmove.");
return 0;
}
set_lv_segment_area_lv(seg, s, lv_mirr, start_le);
extent_count += seg->area_len;
lv->status |= LOCKED;
break;
}
}
}
log_verbose("Moving %u extents of logical volume %s/%s", extent_count,
lv->vg->name, lv->name);
return 1;
}
/* Remove a temporary mirror */
int remove_pvmove_mirrors(struct volume_group *vg,
struct logical_volume *lv_mirr)
{
struct list *lvh, *segh;
struct logical_volume *lv1;
struct lv_segment *seg, *mir_seg;
uint32_t s, c;
/* Loop through all LVs except the temporary mirror */
list_iterate(lvh, &vg->lvs) {
lv1 = list_item(lvh, struct lv_list)->lv;
if (lv1 == lv_mirr)
continue;
/* Find all segments that point at the temporary mirror */
list_iterate(segh, &lv1->segments) {
seg = list_item(segh, struct lv_segment);
for (s = 0; s < seg->area_count; s++) {
if (seg->area[s].type != AREA_LV ||
seg->area[s].u.lv.lv != lv_mirr)
continue;
/* Find the mirror segment pointed at */
if (!(mir_seg = find_seg_by_le(lv_mirr,
seg->area[s].
u.lv.le))) {
/* FIXME Error message */
log_error("No segment found with LE");
return 0;
}
/* Check the segment params are compatible */
/* FIXME Improve error mesg & remove restrcn */
if ((!(mir_seg->segtype->flags
& SEG_AREAS_MIRRORED)) ||
!(mir_seg->status & PVMOVE) ||
mir_seg->le != seg->area[s].u.lv.le ||
mir_seg->area_count != 2 ||
mir_seg->area_len != seg->area_len) {
log_error("Incompatible segments");
return 0;
}
/* Replace original segment with newly-mirrored
* area (or original if reverting)
*/
if (mir_seg->extents_copied ==
mir_seg->area_len)
c = 1;
else
c = 0;
if (!set_lv_segment_area_pv(seg, s,
mir_seg->area[c].u.pv.pvseg->pv,
mir_seg->area[c].u.pv.pvseg->pe)) {
stack;
return 0;
}
/* Replace mirror with old area */
if (!
(mir_seg->segtype =
get_segtype_from_string(vg->cmd,
"striped"))) {
log_error("Missing striped segtype");
return 0;
}
mir_seg->area_count = 1;
/* FIXME Assumes only one pvmove at a time! */
lv1->status &= ~LOCKED;
}
}
if (!lv_merge_segments(lv1))
stack;
}
return 1;
}
const char *get_pvmove_pvname_from_lv_mirr(struct logical_volume *lv_mirr)
{
struct list *segh;
struct lv_segment *seg;
list_iterate(segh, &lv_mirr->segments) {
seg = list_item(segh, struct lv_segment);
if (!(seg->segtype->flags & SEG_AREAS_MIRRORED))
continue;
if (seg->area[0].type != AREA_PV)
continue;
return dev_name(seg->area[0].u.pv.pvseg->pv->dev);
}
return NULL;
}
const char *get_pvmove_pvname_from_lv(struct logical_volume *lv)
{
struct list *segh;
struct lv_segment *seg;
uint32_t s;
list_iterate(segh, &lv->segments) {
seg = list_item(segh, struct lv_segment);
for (s = 0; s < seg->area_count; s++) {
if (seg->area[s].type != AREA_LV)
continue;
return get_pvmove_pvname_from_lv_mirr(seg->area[s].u.lv.lv);
}
}
return NULL;
}
struct logical_volume *find_pvmove_lv(struct volume_group *vg,
struct device *dev,
uint32_t lv_type)
{
struct list *lvh, *segh;
struct logical_volume *lv;
struct lv_segment *seg;
/* Loop through all LVs */
list_iterate(lvh, &vg->lvs) {
lv = list_item(lvh, struct lv_list)->lv;
if (!(lv->status & lv_type))
continue;
/* Check segment origins point to pvname */
list_iterate(segh, &lv->segments) {
seg = list_item(segh, struct lv_segment);
if (seg->area[0].type != AREA_PV)
continue;
if (seg->area[0].u.pv.pvseg->pv->dev != dev)
continue;
return lv;
}
}
return NULL;
}
struct logical_volume *find_pvmove_lv_from_pvname(struct cmd_context *cmd,
struct volume_group *vg,
const char *name,
uint32_t lv_type)
{
struct physical_volume *pv;
if (!(pv = find_pv_by_name(cmd, name))) {
stack;
return NULL;
}
return find_pvmove_lv(vg, pv->dev, lv_type);
}
struct list *lvs_using_lv(struct cmd_context *cmd, struct volume_group *vg,
struct logical_volume *lv)
{
struct list *lvh, *segh, *lvs;
struct logical_volume *lv1;
struct lv_list *lvl;
struct lv_segment *seg;
uint32_t s;
if (!(lvs = pool_alloc(cmd->mem, sizeof(*lvs)))) {
log_error("lvs list alloc failed");
return NULL;
}
list_init(lvs);
/* Loop through all LVs except the one supplied */
list_iterate(lvh, &vg->lvs) {
lv1 = list_item(lvh, struct lv_list)->lv;
if (lv1 == lv)
continue;
/* Find whether any segment points at the supplied LV */
list_iterate(segh, &lv1->segments) {
seg = list_item(segh, struct lv_segment);
for (s = 0; s < seg->area_count; s++) {
if (seg->area[s].type != AREA_LV ||
seg->area[s].u.lv.lv != lv)
continue;
if (!(lvl = pool_alloc(cmd->mem, sizeof(*lvl)))) {
log_error("lv_list alloc failed");
return NULL;
}
lvl->lv = lv1;
list_add(lvs, &lvl->list);
goto next_lv;
}
}
next_lv:
;
}
return lvs;
}
float copy_percent(struct logical_volume *lv_mirr)
{
uint32_t numerator = 0u, denominator = 0u;
struct list *segh;
struct lv_segment *seg;
list_iterate(segh, &lv_mirr->segments) {
seg = list_item(segh, struct lv_segment);
denominator += seg->area_len;
if (seg->segtype->flags & SEG_AREAS_MIRRORED)
numerator += seg->extents_copied;
else
numerator += seg->area_len;
}
return denominator ? (float) numerator *100 / denominator : 100.0;
}