1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-01-02 01:18:26 +03:00

A pvresize implementation (Zak Kipling).

This commit is contained in:
Alasdair Kergon 2005-10-31 02:37:29 +00:00
parent fea5c22b7c
commit 3bf5f6f575
10 changed files with 348 additions and 11 deletions

View File

@ -1,5 +1,6 @@
Version 2.02.00 -
===================================
A pvresize implementation.
Fix contiguous allocation when there are no preceding segments.
Add mirror_seg pointer to lv_segment struct.
Only keep a device open if it's known to belong to a locked VG.

View File

@ -1687,7 +1687,7 @@ struct format_type *create_text_format(struct cmd_context *cmd)
fmt->name = FMT_TEXT_NAME;
fmt->alias = FMT_TEXT_ALIAS;
fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS | FMT_PRECOMMIT |
FMT_UNLIMITED_VOLS;
FMT_UNLIMITED_VOLS | FMT_RESIZE_PV;
if (!(mda_lists = dm_malloc(sizeof(struct mda_lists)))) {
log_error("Failed to allocate dir_list");

View File

@ -37,6 +37,8 @@ struct data_area_list {
/* On disk */
struct pv_header {
uint8_t pv_uuid[ID_LEN];
/* This size can be overridden if PV belongs to a VG */
uint64_t device_size_xl; /* Bytes */
/* NULL-terminated list of data areas followed by */

View File

@ -58,6 +58,7 @@
#define MIRROR_LOG 0x00020000 /* LV */
#define MIRROR_IMAGE 0x00040000 /* LV */
#define ACTIVATE_EXCL 0x00080000 /* LV - internal use only */
#define PRECOMMITTED 0x00100000 /* VG - internal use only */
#define LVM_READ 0x00000100 /* LV VG */
#define LVM_WRITE 0x00000200 /* LV VG */
@ -72,6 +73,7 @@
#define FMT_RESTRICTED_LVIDS 0x00000010 /* LVID <= 255 */
#define FMT_ORPHAN_ALLOCATABLE 0x00000020 /* Orphan PV allocatable? */
#define FMT_PRECOMMIT 0x00000040 /* Supports pre-commit? */
#define FMT_RESIZE_PV 0x00000080 /* Supports pvresize? */
typedef enum {
ALLOC_INVALID,
@ -402,10 +404,10 @@ int vg_commit(struct volume_group *vg);
int vg_revert(struct volume_group *vg);
struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
int *consistent);
struct volume_group *vg_read_precommitted(struct cmd_context *cmd,
const char *vg_name,
int *consistent);
struct volume_group *vg_read_by_vgid(struct cmd_context *cmd, const char *vgid);
// struct volume_group *vg_read_precommitted(struct cmd_context *cmd,
// const char *vg_name,
// int *consistent);
// struct volume_group *vg_read_by_vgid(struct cmd_context *cmd, const char *vgid);
struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name,
struct list *mdas, uint64_t *label_sector,
int warnings);
@ -428,6 +430,8 @@ struct physical_volume *pv_create(const struct format_type *fmt,
uint32_t existing_extent_size,
int pvmetadatacopies,
uint64_t pvmetadatasize, struct list *mdas);
int pv_resize(struct physical_volume *pv, struct volume_group *vg,
uint32_t new_pe_count);
struct volume_group *vg_create(struct cmd_context *cmd, const char *name,
uint32_t extent_size, uint32_t max_pv,
@ -491,7 +495,8 @@ struct volume_group *find_vg_with_lv(const char *lv_name);
/* Find LV with given lvid (used during activation) */
struct logical_volume *lv_from_lvid(struct cmd_context *cmd,
const char *lvid_s);
const char *lvid_s,
int precommit);
/* FIXME Merge these functions with ones above */
struct physical_volume *find_pv(struct volume_group *vg, struct device *dev);

View File

@ -301,3 +301,98 @@ int check_pv_segments(struct volume_group *vg)
return ret;
}
static int _reduce_pv(struct physical_volume *pv, struct volume_group *vg, uint32_t new_pe_count)
{
struct pv_segment *peg, *pegt;
uint32_t old_pe_count = pv->pe_count;
if (new_pe_count < pv->pe_alloc_count) {
log_error("%s: cannot resize to %" PRIu32 " extents "
"as %" PRIu32 " are allocated.",
dev_name(pv->dev), new_pe_count,
pv->pe_alloc_count);
return 0;
}
/* Check PEs to be removed are not already allocated */
list_iterate_items(peg, &pv->segments) {
if (peg->pe + peg->len <= new_pe_count)
continue;
if (peg->lvseg) {
log_error("%s: cannot resize to %" PRIu32 " extents as "
"later ones are allocated.",
dev_name(pv->dev), new_pe_count);
return 0;
}
}
if (!pv_split_segment(pv, new_pe_count)) {
stack;
return 0;
}
list_iterate_items_safe(peg, pegt, &pv->segments) {
if (peg->pe + peg->len > new_pe_count)
list_del(&peg->list);
}
pv->pe_count = new_pe_count;
vg->extent_count -= (old_pe_count - new_pe_count);
vg->free_count -= (old_pe_count - new_pe_count);
return 1;
}
static int _extend_pv(struct physical_volume *pv, struct volume_group *vg,
uint32_t new_pe_count)
{
struct pv_segment *peg;
uint32_t old_pe_count = pv->pe_count;
if ((uint64_t) new_pe_count * pv->pe_size > pv->size ) {
log_error("%s: cannot resize to %" PRIu32 " extents as there "
"is only room for %" PRIu64 ".", dev_name(pv->dev),
new_pe_count, pv->size / pv->pe_size);
return 0;
}
peg = _alloc_pv_segment(pv->fmt->cmd->mem, pv,
old_pe_count,
new_pe_count - old_pe_count,
NULL, 0);
list_add(&pv->segments, &peg->list);
pv->pe_count = new_pe_count;
vg->extent_count += (new_pe_count - old_pe_count);
vg->free_count += (new_pe_count - old_pe_count);
return 1;
}
/*
* Resize a PV in a VG, adding or removing segments as needed.
* New size must fit within pv->size.
*/
int pv_resize(struct physical_volume *pv,
struct volume_group *vg,
uint32_t new_pe_count)
{
if ((new_pe_count == pv->pe_count)) {
log_verbose("No change to size of physical volume %s.",
dev_name(pv->dev));
return 1;
}
log_verbose("Resizing physical volume %s from %" PRIu32
" to %" PRIu32 " extents.",
dev_name(pv->dev), pv->pe_count, new_pe_count);
if (new_pe_count > pv->pe_count)
return _extend_pv(pv, vg, new_pe_count);
else
return _reduce_pv(pv, vg, new_pe_count);
}

View File

@ -19,10 +19,10 @@ VPATH = @srcdir@
MAN5=lvm.conf.5
MAN8=lvchange.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 lvmchange.8 \
lvmdiskscan.8 lvreduce.8 lvremove.8 lvrename.8 lvresize.8 lvs.8 \
lvscan.8 pvchange.8 pvcreate.8 pvdisplay.8 pvmove.8 pvremove.8 pvs.8 \
pvscan.8 vgcfgbackup.8 vgcfgrestore.8 vgchange.8 vgck.8 vgcreate.8 \
vgconvert.8 vgdisplay.8 vgexport.8 vgextend.8 vgimport.8 \
vgmerge.8 vgmknodes.8 vgreduce.8 vgremove.8 vgrename.8 \
lvscan.8 pvchange.8 pvcreate.8 pvdisplay.8 pvmove.8 pvremove.8 \
pvresize.8 pvs.8 pvscan.8 vgcfgbackup.8 vgcfgrestore.8 vgchange.8 \
vgck.8 vgcreate.8 vgconvert.8 vgdisplay.8 vgexport.8 vgextend.8 \
vgimport.8 vgmerge.8 vgmknodes.8 vgreduce.8 vgremove.8 vgrename.8 \
vgs.8 vgscan.8 vgsplit.8
MAN8CLUSTER=clvmd.8
MAN5DIR=${mandir}/man5

View File

@ -42,6 +42,7 @@ SOURCES =\
pvdisplay.c \
pvmove.c \
pvremove.c \
pvresize.c \
pvscan.c \
reporter.c \
segtypes.c \

View File

@ -373,6 +373,19 @@ xx(pvchange,
all_ARG, allocatable_ARG, allocation_ARG, autobackup_ARG, deltag_ARG,
addtag_ARG, test_ARG, uuid_ARG)
xx(pvresize,
"Resize physical volume(s)",
"pvresize " "\n"
"\t[-d|--debug]" "\n"
"\t[-h|-?|--help] " "\n"
"\t[--setphysicalvolumesize PhysicalVolumeSize[kKmMgGtT]" "\n"
"\t[-t|--test] " "\n"
"\t[-v|--verbose] " "\n"
"\t[--version] " "\n"
"\tPhysicalVolume [PhysicalVolume...]\n",
physicalvolumesize_ARG, test_ARG)
xx(pvcreate,
"Initialize physical volume(s) for use by LVM",
"pvcreate " "\n"

221
tools/pvresize.c Normal file
View File

@ -0,0 +1,221 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
* Copyright (C) 2005 Zak Kipling. 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 "tools.h"
struct pvresize_params {
uint64_t new_size;
unsigned done;
unsigned total;
};
static int _pvresize_single(struct cmd_context *cmd,
struct volume_group *vg,
struct physical_volume *pv,
void *handle)
{
struct pv_list *pvl;
int consistent = 1;
uint64_t size = 0;
uint32_t new_pe_count = 0;
struct list mdas;
const char *pv_name = dev_name(pv->dev);
struct pvresize_params *params = (struct pvresize_params *) handle;
const char *vg_name;
list_init(&mdas);
params->total++;
if (!*pv->vg_name) {
vg_name = ORPHAN;
if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
log_error("Can't get lock for orphans");
return ECMD_FAILED;
}
if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1))) {
unlock_vg(cmd, vg_name);
log_error("Unable to read PV \"%s\"", pv_name);
return ECMD_FAILED;
}
/* FIXME Create function to test compatibility properly */
if (list_size(&mdas) > 1) {
log_error("%s: too many metadata areas for pvresize",
pv_name);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
} else {
vg_name = pv->vg_name;
if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
log_error("Can't get lock for %s", pv->vg_name);
return ECMD_FAILED;
}
if (!(vg = vg_read(cmd, vg_name, &consistent))) {
unlock_vg(cmd, vg_name);
log_error("Unable to find volume group of \"%s\"",
pv_name);
return ECMD_FAILED;
}
if (vg->status & EXPORTED_VG) {
unlock_vg(cmd, vg_name);
log_error("Volume group \"%s\" is exported", vg->name);
return ECMD_FAILED;
}
if (!(vg->status & LVM_WRITE)) {
unlock_vg(cmd, pv->vg_name);
log_error("Volume group \"%s\" is read-only", vg->name);
return ECMD_FAILED;
}
if (!(pvl = find_pv_in_vg(vg, pv_name))) {
unlock_vg(cmd, vg_name);
log_error("Unable to find \"%s\" in volume group \"%s\"",
pv_name, vg->name);
return ECMD_FAILED;
}
pv = pvl->pv;
if (!archive(vg))
return ECMD_FAILED;
}
if (!(pv->fmt->features & FMT_RESIZE_PV)) {
log_error("Physical volume %s format does not support resizing.",
pv_name);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
/* Get new size */
if (!dev_get_size(pv->dev, &size)) {
log_error("%s: Couldn't get size.", pv_name);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
if (params->new_size) {
if (params->new_size > size)
log_print("WARNING: %s: Overriding real size. "
"You could lose data.", pv_name);
log_verbose("%s: Pretending size is %" PRIu64 " not %" PRIu64
" sectors.", pv_name, params->new_size, pv->size);
size = params->new_size;
}
if (size < PV_MIN_SIZE) {
log_error("%s: Size must exceed minimum of %ld sectors.",
pv_name, PV_MIN_SIZE);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
if (size < pv->pe_start) {
log_error("%s: Size must exceed physical extent start of "
"%" PRIu64 " sectors.", pv_name, pv->pe_start);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
pv->size = size;
if (vg) {
pv->size -= pv->pe_start;
new_pe_count = pv->size / vg->extent_size;
if (!new_pe_count) {
log_error("%s: Size must leave space for at "
"least one physical extent of "
"%" PRIu32 " sectors.", pv_name,
pv->pe_size);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
if (!pv_resize(pv, vg, new_pe_count)) {
stack;
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
}
log_verbose("Resizing volume \"%s\" to %" PRIu64 " sectors.",
pv_name, pv->size);
log_verbose("Updating physical volume \"%s\"", pv_name);
if (*pv->vg_name) {
if (!vg_write(vg) || !vg_commit(vg)) {
unlock_vg(cmd, pv->vg_name);
log_error("Failed to store physical volume \"%s\" in "
"volume group \"%s\"", pv_name, vg->name);
return ECMD_FAILED;
}
backup(vg);
unlock_vg(cmd, vg_name);
} else {
if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
unlock_vg(cmd, ORPHAN);
log_error("Failed to store physical volume \"%s\"",
pv_name);
return ECMD_FAILED;
}
unlock_vg(cmd, vg_name);
}
log_print("Physical volume \"%s\" changed", pv_name);
params->done++;
return 1;
}
int pvresize(struct cmd_context *cmd, int argc, char **argv)
{
struct pvresize_params params;
int ret;
if (!argc) {
log_error("Please supply physical volume(s)");
return EINVALID_CMD_LINE;
}
if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) {
log_error("Physical volume size may not be negative");
return 0;
}
params.new_size = arg_uint64_value(cmd, physicalvolumesize_ARG,
UINT64_C(0)) * 2;
params.done = 0;
params.total = 0;
ret = process_each_pv(cmd, argc, argv, NULL, &params, _pvresize_single);
log_print("%d physical volume(s) resized / %d physical volume(s) "
"not resized", params.done, params.total - params.done);
return ret;
}

View File

@ -19,7 +19,6 @@
/*int e2fsadm(struct cmd_context *cmd, int argc, char **argv) unimplemented*/
int lvmsadc(struct cmd_context *cmd, int argc, char **argv) unimplemented
int lvmsar(struct cmd_context *cmd, int argc, char **argv) unimplemented
int pvresize(struct cmd_context *cmd, int argc, char **argv) unimplemented
int pvdata(struct cmd_context *cmd, int argc, char **argv) {
log_error("There's no 'pvdata' command in LVM2.");