mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
fcbef05aae
Hmm rpmlint suggest fsf is using a different address these days, so lets keep it up-to-date
380 lines
8.6 KiB
C
380 lines
8.6 KiB
C
/*
|
|
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004-2007 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 Lesser General Public License v.2.1.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "lib.h"
|
|
#include "metadata.h"
|
|
#include "disk-rep.h"
|
|
#include "lv_alloc.h"
|
|
#include "display.h"
|
|
#include "segtype.h"
|
|
|
|
/*
|
|
* After much thought I have decided it is easier,
|
|
* and probably no less efficient, to convert the
|
|
* pe->le map to a full le->pe map, and then
|
|
* process this to get the segments form that
|
|
* we're after. Any code which goes directly from
|
|
* the pe->le map to segments would be gladly
|
|
* accepted, if it is less complicated than this
|
|
* file.
|
|
*/
|
|
struct pe_specifier {
|
|
struct physical_volume *pv;
|
|
uint32_t pe;
|
|
};
|
|
|
|
struct lv_map {
|
|
struct logical_volume *lv;
|
|
uint32_t stripes;
|
|
uint32_t stripe_size;
|
|
struct pe_specifier *map;
|
|
};
|
|
|
|
static struct dm_hash_table *_create_lv_maps(struct dm_pool *mem,
|
|
struct volume_group *vg)
|
|
{
|
|
struct dm_hash_table *maps = dm_hash_create(32);
|
|
struct lv_list *ll;
|
|
struct lv_map *lvm;
|
|
|
|
if (!maps) {
|
|
log_error("Unable to create hash table for holding "
|
|
"extent maps.");
|
|
return NULL;
|
|
}
|
|
|
|
dm_list_iterate_items(ll, &vg->lvs) {
|
|
if (ll->lv->status & SNAPSHOT)
|
|
continue;
|
|
|
|
if (!(lvm = dm_pool_alloc(mem, sizeof(*lvm))))
|
|
goto_bad;
|
|
|
|
lvm->lv = ll->lv;
|
|
/*
|
|
* Alloc 1 extra element, so the loop in _area_length() and
|
|
* _check_stripe() finds the last map member as noncontinuous.
|
|
*/
|
|
if (!(lvm->map = dm_pool_zalloc(mem, sizeof(*lvm->map)
|
|
* (ll->lv->le_count + 1))))
|
|
goto_bad;
|
|
|
|
if (!dm_hash_insert(maps, ll->lv->name, lvm))
|
|
goto_bad;
|
|
}
|
|
|
|
return maps;
|
|
|
|
bad:
|
|
dm_hash_destroy(maps);
|
|
return NULL;
|
|
}
|
|
|
|
static int _fill_lv_array(struct lv_map **lvs,
|
|
struct dm_hash_table *maps, struct disk_list *dl)
|
|
{
|
|
struct lvd_list *ll;
|
|
struct lv_map *lvm;
|
|
|
|
memset(lvs, 0, sizeof(*lvs) * MAX_LV);
|
|
|
|
dm_list_iterate_items(ll, &dl->lvds) {
|
|
if (!(lvm = dm_hash_lookup(maps, strrchr((char *)ll->lvd.lv_name, '/')
|
|
+ 1))) {
|
|
log_error("Physical volume (%s) contains an "
|
|
"unknown logical volume (%s).",
|
|
dev_name(dl->dev), ll->lvd.lv_name);
|
|
return 0;
|
|
}
|
|
|
|
lvm->stripes = ll->lvd.lv_stripes;
|
|
lvm->stripe_size = ll->lvd.lv_stripesize;
|
|
|
|
lvs[ll->lvd.lv_number] = lvm;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _fill_maps(struct dm_hash_table *maps, struct volume_group *vg,
|
|
struct dm_list *pvds)
|
|
{
|
|
struct disk_list *dl;
|
|
struct physical_volume *pv;
|
|
struct lv_map *lvms[MAX_LV], *lvm;
|
|
struct pe_disk *e;
|
|
uint32_t i, lv_num, le;
|
|
|
|
dm_list_iterate_items(dl, pvds) {
|
|
if (!(pv = find_pv(vg, dl->dev))) {
|
|
log_error("PV %s not found.", dl->dev->pvid);
|
|
return 0;
|
|
}
|
|
e = dl->extents;
|
|
|
|
/* build an array of lv's for this pv */
|
|
if (!_fill_lv_array(lvms, maps, dl))
|
|
return_0;
|
|
|
|
for (i = 0; i < dl->pvd.pe_total; i++) {
|
|
lv_num = e[i].lv_num;
|
|
|
|
if (lv_num == UNMAPPED_EXTENT)
|
|
continue;
|
|
|
|
else {
|
|
lv_num--;
|
|
lvm = lvms[lv_num];
|
|
|
|
if (!lvm) {
|
|
log_error("Invalid LV in extent map "
|
|
"(PV %s, PE %" PRIu32
|
|
", LV %" PRIu32
|
|
", LE %" PRIu32 ")",
|
|
dev_name(pv->dev), i,
|
|
lv_num, e[i].le_num);
|
|
return 0;
|
|
}
|
|
|
|
le = e[i].le_num;
|
|
|
|
if (le >= lvm->lv->le_count) {
|
|
log_error("logical extent number "
|
|
"out of bounds");
|
|
return 0;
|
|
}
|
|
|
|
if (lvm->map[le].pv) {
|
|
log_error("logical extent (%u) "
|
|
"already mapped.", le);
|
|
return 0;
|
|
}
|
|
|
|
lvm->map[le].pv = pv;
|
|
lvm->map[le].pe = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _check_single_map(struct lv_map *lvm)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < lvm->lv->le_count; i++) {
|
|
if (!lvm->map[i].pv) {
|
|
log_error("Logical volume (%s) contains an incomplete "
|
|
"mapping table.", lvm->lv->name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _check_maps_are_complete(struct dm_hash_table *maps)
|
|
{
|
|
struct dm_hash_node *n;
|
|
struct lv_map *lvm;
|
|
|
|
for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) {
|
|
lvm = (struct lv_map *) dm_hash_get_data(maps, n);
|
|
|
|
if (!_check_single_map(lvm))
|
|
return_0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static uint32_t _area_length(struct lv_map *lvm, uint32_t le)
|
|
{
|
|
uint32_t len = 0;
|
|
|
|
do
|
|
len++;
|
|
while ((lvm->map[le + len].pv == lvm->map[le].pv) &&
|
|
(lvm->map[le].pv &&
|
|
lvm->map[le + len].pe == lvm->map[le].pe + len));
|
|
|
|
return len;
|
|
}
|
|
|
|
static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm)
|
|
{
|
|
uint32_t le = 0, len;
|
|
struct lv_segment *seg;
|
|
struct segment_type *segtype;
|
|
|
|
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED)))
|
|
return_0;
|
|
|
|
while (le < lvm->lv->le_count) {
|
|
len = _area_length(lvm, le);
|
|
|
|
if (!(seg = alloc_lv_segment(segtype, lvm->lv, le, len, 0, 0,
|
|
NULL, 1, len, 0, 0, 0, NULL))) {
|
|
log_error("Failed to allocate linear segment.");
|
|
return 0;
|
|
}
|
|
|
|
if (!set_lv_segment_area_pv(seg, 0, lvm->map[le].pv,
|
|
lvm->map[le].pe))
|
|
return_0;
|
|
|
|
dm_list_add(&lvm->lv->segments, &seg->list);
|
|
|
|
le += seg->len;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _check_stripe(struct lv_map *lvm, uint32_t area_count,
|
|
uint32_t area_len, uint32_t base_le,
|
|
uint32_t total_area_len)
|
|
{
|
|
uint32_t st;
|
|
|
|
/*
|
|
* Is the next physical extent in every stripe adjacent to the last?
|
|
*/
|
|
for (st = 0; st < area_count; st++)
|
|
if ((lvm->map[base_le + st * total_area_len + area_len].pv !=
|
|
lvm->map[base_le + st * total_area_len].pv) ||
|
|
(lvm->map[base_le + st * total_area_len].pv &&
|
|
lvm->map[base_le + st * total_area_len + area_len].pe !=
|
|
lvm->map[base_le + st * total_area_len].pe + area_len))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _read_stripes(struct cmd_context *cmd, struct lv_map *lvm)
|
|
{
|
|
uint32_t st, first_area_le = 0, total_area_len;
|
|
uint32_t area_len;
|
|
struct lv_segment *seg;
|
|
struct segment_type *segtype;
|
|
|
|
/*
|
|
* Work out overall striped length
|
|
*/
|
|
if (lvm->lv->le_count % lvm->stripes) {
|
|
log_error("Number of stripes (%u) incompatible "
|
|
"with logical extent count (%u) for %s",
|
|
lvm->stripes, lvm->lv->le_count, lvm->lv->name);
|
|
}
|
|
|
|
total_area_len = lvm->lv->le_count / lvm->stripes;
|
|
|
|
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED)))
|
|
return_0;
|
|
|
|
while (first_area_le < total_area_len) {
|
|
area_len = 1;
|
|
|
|
/*
|
|
* Find how many extents are contiguous in all stripes
|
|
* and so can form part of this segment
|
|
*/
|
|
while (_check_stripe(lvm, lvm->stripes,
|
|
area_len, first_area_le, total_area_len))
|
|
area_len++;
|
|
|
|
if (!(seg = alloc_lv_segment(segtype, lvm->lv,
|
|
lvm->stripes * first_area_le,
|
|
lvm->stripes * area_len,
|
|
0, lvm->stripe_size, NULL,
|
|
lvm->stripes,
|
|
area_len, 0, 0, 0, NULL))) {
|
|
log_error("Failed to allocate striped segment.");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set up start positions of each stripe in this segment
|
|
*/
|
|
for (st = 0; st < seg->area_count; st++)
|
|
if (!set_lv_segment_area_pv(seg, st,
|
|
lvm->map[first_area_le + st * total_area_len].pv,
|
|
lvm->map[first_area_le + st * total_area_len].pe))
|
|
return_0;
|
|
|
|
dm_list_add(&lvm->lv->segments, &seg->list);
|
|
|
|
first_area_le += area_len;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _build_segments(struct cmd_context *cmd, struct lv_map *lvm)
|
|
{
|
|
return (lvm->stripes > 1 ? _read_stripes(cmd, lvm) :
|
|
_read_linear(cmd, lvm));
|
|
}
|
|
|
|
static int _build_all_segments(struct cmd_context *cmd, struct dm_hash_table *maps)
|
|
{
|
|
struct dm_hash_node *n;
|
|
struct lv_map *lvm;
|
|
|
|
for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) {
|
|
lvm = (struct lv_map *) dm_hash_get_data(maps, n);
|
|
if (!_build_segments(cmd, lvm))
|
|
return_0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int import_extents(struct cmd_context *cmd, struct volume_group *vg,
|
|
struct dm_list *pvds)
|
|
{
|
|
int r = 0;
|
|
struct dm_pool *scratch = dm_pool_create("lvm1 import_extents", 10 * 1024);
|
|
struct dm_hash_table *maps;
|
|
|
|
if (!scratch)
|
|
return_0;
|
|
|
|
if (!(maps = _create_lv_maps(scratch, vg))) {
|
|
log_error("Couldn't allocate logical volume maps.");
|
|
goto out;
|
|
}
|
|
|
|
if (!_fill_maps(maps, vg, pvds)) {
|
|
log_error("Couldn't fill logical volume maps.");
|
|
goto out;
|
|
}
|
|
|
|
if (!_check_maps_are_complete(maps) && !(vg->status & PARTIAL_VG))
|
|
goto_out;
|
|
|
|
if (!_build_all_segments(cmd, maps)) {
|
|
log_error("Couldn't build extent segments.");
|
|
goto out;
|
|
}
|
|
r = 1;
|
|
|
|
out:
|
|
if (maps)
|
|
dm_hash_destroy(maps);
|
|
dm_pool_destroy(scratch);
|
|
return r;
|
|
}
|