1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/lib/metadata/merge.c
Alasdair Kergon cf6dd25126 tagging
2004-03-08 17:19:15 +00:00

200 lines
4.3 KiB
C

/*
* Copyright (C) 2001 Sistina Software
*
* This file is released under the LGPL.
*/
#include "lib.h"
#include "metadata.h"
#include "toolcontext.h"
#include "lv_alloc.h"
#include "str_list.h"
/*
* Test whether two segments could be merged by the current merging code
*/
static int _segments_compatible(struct lv_segment *first,
struct lv_segment *second)
{
uint32_t width;
unsigned s;
/* FIXME Relax the seg type restriction */
if (!first || !second ||
(first->type != SEG_STRIPED) || (second->type != first->type) ||
(first->area_count != second->area_count) ||
(first->stripe_size != second->stripe_size))
return 0;
for (s = 0; s < first->area_count; s++) {
/* FIXME Relax this to first area type != second area type */
/* plus the additional AREA_LV checks needed */
if ((first->area[s].type != AREA_PV) ||
(second->area[s].type != AREA_PV))
return 0;
width = first->area_len;
if ((first->area[s].u.pv.pv != second->area[s].u.pv.pv) ||
(first->area[s].u.pv.pe + width != second->area[s].u.pv.pe))
return 0;
}
if (!str_list_lists_equal(&first->tags, &second->tags))
return 0;
return 1;
}
/*
* Attempt to merge two adjacent segments.
* Currently only supports SEG_STRIPED on AREA_PV.
* Returns success if successful, in which case 'first'
* gets adjusted to contain both areas.
*/
static int _merge(struct lv_segment *first, struct lv_segment *second)
{
if (!_segments_compatible(first, second))
return 0;
first->len += second->len;
first->area_len += second->area_len;
return 1;
}
int lv_merge_segments(struct logical_volume *lv)
{
struct list *segh, *t;
struct lv_segment *current, *prev = NULL;
list_iterate_safe(segh, t, &lv->segments) {
current = list_item(segh, struct lv_segment);
if (_merge(prev, current))
list_del(&current->list);
else
prev = current;
}
return 1;
}
/*
* Verify that an LV's segments are consecutive, complete and don't overlap.
*/
int lv_check_segments(struct logical_volume *lv)
{
struct lv_segment *seg;
uint32_t le = 0;
unsigned seg_count = 0;
list_iterate_items(seg, &lv->segments) {
seg_count++;
if (seg->le != le) {
log_error("LV %s invalid: segment %u should begin at "
"LE %" PRIu32 " (found %" PRIu32 ").",
lv->name, seg_count, le, seg->le);
return 0;
}
le += seg->len;
}
return 1;
}
/*
* Split the supplied segment at the supplied logical extent
*/
static int _lv_split_segment(struct logical_volume *lv, struct lv_segment *seg,
uint32_t le)
{
size_t len;
struct lv_segment *split_seg;
uint32_t s;
uint32_t offset = le - seg->le;
if (seg->type == SEG_SNAPSHOT) {
log_error("Unable to split the snapshot segment at LE %" PRIu32
" in LV %s", le, lv->name);
return 0;
}
/* Clone the existing segment */
if (!(split_seg = alloc_lv_segment(lv->vg->cmd->mem,
seg->area_count))) {
log_error("Couldn't allocate new LV segment.");
return 0;
}
len = sizeof(*seg) + (seg->area_count * sizeof(seg->area[0]));
memcpy(split_seg, seg, len);
if (!str_list_dup(lv->vg->cmd->mem, &split_seg->tags, &seg->tags)) {
log_error("LV segment tags duplication failed");
return 0;
}
/* In case of a striped segment, the offset has to be / stripes */
if (seg->type == SEG_STRIPED)
offset /= seg->area_count;
/* Adjust the PV mapping */
for (s = 0; s < seg->area_count; s++) {
/* Split area at the offset */
switch (seg->area[s].type) {
case AREA_LV:
split_seg->area[s].u.lv.le =
seg->area[s].u.lv.le + offset;
break;
case AREA_PV:
split_seg->area[s].u.pv.pe =
seg->area[s].u.pv.pe + offset;
break;
default:
log_error("Unrecognised segment type %u",
seg->area[s].type);
return 0;
}
}
split_seg->area_len = seg->area_len - offset;
seg->area_len = offset;
/* Add split off segment to the list _after_ the original one */
list_add_h(&seg->list, &split_seg->list);
return 1;
}
/*
* Ensure there's a segment boundary at the given logical extent
*/
int lv_split_segment(struct logical_volume *lv, uint32_t le)
{
struct lv_segment *seg;
if (!(seg = find_seg_by_le(lv, le))) {
log_error("Segment with extent %" PRIu32 " in LV %s not found",
le, lv->name);
return 0;
}
/* This is a segment start already */
if (le == seg->le)
return 1;
if (!_lv_split_segment(lv, seg, le)) {
stack;
return 0;
}
return 1;
}