blob: 380a983031a9b2051005df663d6693815e30f0b9 [file] [log] [blame]
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2006 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 "disk-rep.h"
/*
* Only works with powers of 2.
*/
static uint32_t _round_up(uint32_t n, uint32_t size)
{
size--;
return (n + size) & ~size;
}
/* Unused.
static uint32_t _div_up(uint32_t n, uint32_t size)
{
return _round_up(n, size) / size;
}
*/
/*
* Each chunk of metadata should be aligned to
* METADATA_ALIGN.
*/
static uint32_t _next_base(struct data_area *area)
{
return _round_up(area->base + area->size, METADATA_ALIGN);
}
/*
* Quick calculation based on pe_start.
*/
static int _adjust_pe_on_disk(struct pv_disk *pvd)
{
uint32_t pe_start = pvd->pe_start << SECTOR_SHIFT;
if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size)
return 0;
pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
return 1;
}
static void _calc_simple_layout(struct pv_disk *pvd)
{
pvd->pv_on_disk.base = METADATA_BASE;
pvd->pv_on_disk.size = PV_SIZE;
pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk);
pvd->vg_on_disk.size = VG_SIZE;
pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk);
pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN;
pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk);
pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk);
pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk);
pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk);
}
static int _check_vg_limits(struct disk_list *dl)
{
if (dl->vgd.lv_max > MAX_LV) {
log_error("MaxLogicalVolumes of %d exceeds format limit of %d "
"for VG '%s'", dl->vgd.lv_max, MAX_LV - 1,
dl->pvd.vg_name);
return 0;
}
if (dl->vgd.pv_max > MAX_PV) {
log_error("MaxPhysicalVolumes of %d exceeds format limit of %d "
"for VG '%s'", dl->vgd.pv_max, MAX_PV - 1,
dl->pvd.vg_name);
return 0;
}
return 1;
}
/*
* This assumes pe_count and pe_start have already
* been calculated correctly.
*/
int calculate_layout(struct disk_list *dl)
{
struct pv_disk *pvd = &dl->pvd;
_calc_simple_layout(pvd);
if (!_adjust_pe_on_disk(pvd)) {
log_error("Insufficient space for metadata and PE's.");
return 0;
}
if (!_check_vg_limits(dl))
return 0;
return 1;
}
/*
* The number of extents that can fit on a disk is metadata format dependant.
* pe_start is any existing value for pe_start
*/
int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size,
uint32_t max_extent_count, uint64_t pe_start)
{
struct pv_disk *pvd = dm_malloc(sizeof(*pvd));
uint32_t end;
if (!pvd)
return_0;
/*
* Guess how many extents will fit, bearing in mind that
* one is going to be knocked off at the start of the
* next loop.
*/
if (max_extent_count)
pvd->pe_total = max_extent_count + 1;
else
pvd->pe_total = (pv->size / extent_size);
if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) {
log_error("Too few extents on %s. Try smaller extent size.",
pv_dev_name(pv));
dm_free(pvd);
return 0;
}
do {
pvd->pe_total--;
_calc_simple_layout(pvd);
end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size +
SECTOR_SIZE - 1) >> SECTOR_SHIFT);
if (pe_start && end < pe_start)
end = pe_start;
pvd->pe_start = _round_up(end, LVM1_PE_ALIGN);
} while ((pvd->pe_start + ((uint64_t)pvd->pe_total * extent_size))
> pv->size);
if (pvd->pe_total > MAX_PE_TOTAL) {
log_error("Metadata extent limit (%u) exceeded for %s - "
"%u required", MAX_PE_TOTAL, pv_dev_name(pv),
pvd->pe_total);
dm_free(pvd);
return 0;
}
pv->pe_count = pvd->pe_total;
pv->pe_start = pvd->pe_start;
/* We can't set pe_size here without breaking LVM1 compatibility */
dm_free(pvd);
return 1;
}