mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
25b733809a
Lots of changes/very little testing so far => there'll be bugs! Use 'vgcreate -M text' to create a volume group with its metadata stored in text files. Text format metadata changes should be reasonably atomic, with a (basic) automatic recovery mechanism if the system crashes while a change is in progress. Add a metadata section to lvm.conf to specify multiple directories if you want (recommended) to keep multiple copies of the metadata (eg on different filesystems). e.g. metadata { dirs = ["/etc/lvm/metadata1","/usr/local/lvm/metadata2"] } Plenty of refinements still in the pipeline.
157 lines
3.4 KiB
C
157 lines
3.4 KiB
C
/*
|
|
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
|
*
|
|
* This file is released under the LGPL.
|
|
*/
|
|
|
|
#include "disk-rep.h"
|
|
#include "log.h"
|
|
#include "dbg_malloc.h"
|
|
|
|
/*
|
|
* Only works with powers of 2.
|
|
*/
|
|
static inline ulong _round_up(ulong n, ulong size)
|
|
{
|
|
size--;
|
|
return (n + size) & ~size;
|
|
}
|
|
|
|
static inline ulong _div_up(ulong n, ulong 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_SIZE;
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* It may seem strange to have a struct physical_volume in here,
|
|
* but the number of extents that can fit on a disk *is* metadata
|
|
* format dependant.
|
|
*/
|
|
int calculate_extent_count(struct physical_volume *pv)
|
|
{
|
|
struct pv_disk *pvd = dbg_malloc(sizeof(*pvd));
|
|
uint32_t end;
|
|
|
|
if (!pvd) {
|
|
stack;
|
|
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.
|
|
*/
|
|
pvd->pe_total = (pv->size / pv->pe_size);
|
|
|
|
if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) {
|
|
log_error("Insufficient space for extents on %s",
|
|
dev_name(pv->dev));
|
|
dbg_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_SIZE);
|
|
|
|
pvd->pe_start = _round_up(end, PE_ALIGN);
|
|
|
|
} while ((pvd->pe_start + (pvd->pe_total * pv->pe_size)) > pv->size);
|
|
|
|
if (pvd->pe_total > MAX_PE_TOTAL) {
|
|
log_error("Metadata extent limit (%u) exceeded for %s - "
|
|
"%u required", MAX_PE_TOTAL, dev_name(pv->dev),
|
|
pvd->pe_total);
|
|
dbg_free(pvd);
|
|
return 0;
|
|
}
|
|
|
|
pv->pe_count = pvd->pe_total;
|
|
pv->pe_start = pvd->pe_start;
|
|
dbg_free(pvd);
|
|
return 1;
|
|
}
|