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:
parent
fea5c22b7c
commit
3bf5f6f575
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -42,6 +42,7 @@ SOURCES =\
|
||||
pvdisplay.c \
|
||||
pvmove.c \
|
||||
pvremove.c \
|
||||
pvresize.c \
|
||||
pvscan.c \
|
||||
reporter.c \
|
||||
segtypes.c \
|
||||
|
@ -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
221
tools/pvresize.c
Normal 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, ¶ms, _pvresize_single);
|
||||
|
||||
log_print("%d physical volume(s) resized / %d physical volume(s) "
|
||||
"not resized", params.done, params.total - params.done);
|
||||
|
||||
return ret;
|
||||
}
|
@ -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.");
|
||||
|
Loading…
Reference in New Issue
Block a user