Index: lib/metadata/pv_alloc.h =================================================================== RCS file: /cvs/lvm2/LVM2/lib/metadata/pv_alloc.h,v retrieving revision 1.5 diff -b -u -r1.5 pv_alloc.h --- lib/metadata/pv_alloc.h 16 Oct 2005 23:03:58 -0000 1.5 +++ lib/metadata/pv_alloc.h 28 Oct 2005 20:04:49 -0000 @@ -25,4 +25,8 @@ int check_pv_segments(struct volume_group *vg); void merge_pv_segments(struct pv_segment *peg1, struct pv_segment *peg2); +/* This code is experimental. Do NOT complain if it trashes your LVM system */ +int pv_resize(struct physical_volume *pv, struct volume_group *vg, + uint32_t new_pe_count); + #endif Index: lib/metadata/pv_manip.c =================================================================== RCS file: /cvs/lvm2/LVM2/lib/metadata/pv_manip.c,v retrieving revision 1.8 diff -b -u -r1.8 pv_manip.c --- lib/metadata/pv_manip.c 16 Oct 2005 23:03:58 -0000 1.8 +++ lib/metadata/pv_manip.c 28 Oct 2005 20:04:50 -0000 @@ -301,3 +301,107 @@ return ret; } + + +/* + * Resize a PV, adding or removing segments as needed. + * Update the volume group if supplied. + * New size must fit within pv->size + * + * This code is experimental. Do NOT complain if it trashes your LVM system + */ +int pv_resize(struct physical_volume *pv, + struct volume_group *vg, + uint32_t new_pe_count) +{ + struct pv_segment *peg, *tmp_peg; + uint32_t old_pe_count = pv->pe_count; + + if (new_pe_count < old_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; + } + + log_verbose("Reducing physical volume %s to %" PRIu32 + " extents.", dev_name(pv->dev), new_pe_count); + + if (!pv_split_segment(pv, new_pe_count)) return 0; + + list_iterate_items_safe(peg, tmp_peg, &pv->segments) { + if (peg->pe + peg->len > new_pe_count) { + 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; + } + else { + assert (peg->pe >= new_pe_count); + list_del(&peg->list); + } + } + } + } + else if (new_pe_count > old_pe_count) { + if (pv->size / pv->pe_size < new_pe_count) { + log_error("%s: cannot resize to %" PRIu32 " extents as there " + "is only space for %" PRIu64 ".", dev_name(pv->dev), + new_pe_count, pv->size / pv->pe_size); + } + + log_verbose("Extending physical volume %s to %" PRIu32 + " extents.", dev_name(pv->dev), new_pe_count); + + 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; + } + else { + log_verbose("No change to size of physical volume %s.", + dev_name(pv->dev)); + return 1; + } + + pv->pe_count = new_pe_count; + + if (vg) { + if (strcmp(pv->vg_name, vg->name)) { + log_error("pv_resize internal error: given wrong " + "volume group %s.", vg->name); + return 0; + } + + log_verbose("Changing extent count of volume group %s to " + "match", vg->name); + + if (new_pe_count < old_pe_count) { + assert(vg->extent_count >= old_pe_count); + assert(vg->free_count >= old_pe_count - new_pe_count); + vg->extent_count -= (old_pe_count - new_pe_count); + vg->free_count -= (old_pe_count - new_pe_count); + } + else if (new_pe_count > old_pe_count) { + vg->extent_count += (new_pe_count - old_pe_count); + vg->free_count += (new_pe_count - old_pe_count); + } + } + else { + if (*pv->vg_name) { + log_error("pv_resize internal error: need volume group " + "%s.", pv->vg_name); + return 0; + } + } + + return 1; +} Index: man/Makefile.in =================================================================== RCS file: /cvs/lvm2/LVM2/man/Makefile.in,v retrieving revision 1.14 diff -b -u -r1.14 Makefile.in --- man/Makefile.in 9 Dec 2004 16:57:37 -0000 1.14 +++ man/Makefile.in 28 Oct 2005 20:04:50 -0000 @@ -19,10 +19,10 @@ 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 Index: man/pvresize.8 =================================================================== RCS file: man/pvresize.8 diff -N man/pvresize.8 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ man/pvresize.8 28 Oct 2005 20:04:50 -0000 @@ -0,0 +1,53 @@ +.TH PVCREATE 8 "LVM TOOLS" "Sistina Software UK" \" -*- nroff -*- +.SH NAME +pvresize \- resize a disk or partition in use by LVM +.SH SYNOPSIS +.B pvresize +.RB [ \-d | \-\-debug ] +.RB [ \-h | \-\-help ] +.RB [ \-t | \-\-test ] +.RB [ \-v | \-\-verbose ] +.RB [ \-\-setphysicalvolumesize size ] +.IR PhysicalVolume " [" PhysicalVolume ...] +.SH DESCRIPTION +.B pvresize +resizes +.I PhysicalVolume +which may already be in a volume group and have active logical volumes +allocated on it. +.SH STATUS +.B pvresize +is currently EXPERIMENTAL. Make sure you have a backup and do NOT complain if +it trashes your LVM system. +.SH OPTIONS +See \fBlvm\fP(8) for common options. +.TP +.BR \-\-setphysicalvolumesize " size" +Overrides the automatically-detected size of the PV. Use with care, or +prior to reducing the physical size of the device. +.SH EXAMPLES +Expand the PV on /dev/sda1 after enlarging the partition with fdisk: +.sp +.B pvresize /dev/sda1 +.sp +Shrink the PV on /dev/sda1 prior to shrinking the partition with fdisk +(ensure that the PV size is appropriate for your intended new partition +size): +.sp +.B pvresize --setphysicalvolumesize 40G /dev/sda1 +.sp +.SH RESTRICTIONS +.B pvresize +will refuse to shrink +.I PhysicalVolume +if it has allocated extents after where its new end would be. In the future, +it should relocate these elsewhere in the volume group if there is sufficient +free space, like +.B pvmove +does. +.sp +.B pvresize +probably won't currently work correctly on LVM1 volumes, or PVs with extra +metadata areas. +.SH SEE ALSO +.BR lvm "(8), " pvmove "(8), " lvresize "(8), " fdisk "(8)" Index: tools/Makefile.in =================================================================== RCS file: /cvs/lvm2/LVM2/tools/Makefile.in,v retrieving revision 1.68 diff -b -u -r1.68 Makefile.in --- tools/Makefile.in 6 Jun 2005 17:12:07 -0000 1.68 +++ tools/Makefile.in 28 Oct 2005 20:04:50 -0000 @@ -42,6 +42,7 @@ pvdisplay.c \ pvmove.c \ pvremove.c \ + pvresize.c \ pvscan.c \ reporter.c \ segtypes.c \ Index: tools/commands.h =================================================================== RCS file: /cvs/lvm2/LVM2/tools/commands.h,v retrieving revision 1.75 diff -b -u -r1.75 commands.h --- tools/commands.h 17 Oct 2005 16:41:38 -0000 1.75 +++ tools/commands.h 28 Oct 2005 20:04:52 -0000 @@ -373,6 +373,22 @@ all_ARG, allocatable_ARG, allocation_ARG, autobackup_ARG, deltag_ARG, addtag_ARG, test_ARG, uuid_ARG) +xx(pvresize, + "Resize physical volume(s) in use by LVM [EXPERIMENTAL]", + "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" + "\t\n" + "\tpvresize is EXPERIMENTAL -- ensure you have a backup and\n" + "\tdo NOT complain if it trashes your LVM system!\n", + + test_ARG, physicalvolumesize_ARG) + xx(pvcreate, "Initialize physical volume(s) for use by LVM", "pvcreate " "\n" Index: tools/pvresize.c =================================================================== RCS file: tools/pvresize.c diff -N tools/pvresize.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tools/pvresize.c 28 Oct 2005 20:04:52 -0000 @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. + * Copyright (C) 2004 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" +#include "pv_alloc.h" + +/* This code is experimental. Do NOT complain if it trashes your LVM system */ + +/* FIXME Locking. PVs in VG. */ + +static int _pvresize_single(struct cmd_context *cmd, + struct physical_volume *pv) +{ + struct volume_group *vg = NULL; + struct pv_list *pvl; + struct list mdas; + uint64_t sector; + + const char *pv_name = dev_name(pv->dev); + + int consistent = 1; + uint64_t size = 0; + uint32_t old_pe_count = 0, new_pe_count = 0; + + list_init(&mdas); + + if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) { + log_error("Physical volume size may not be negative"); + return 0; + } + size = arg_uint64_value(cmd, physicalvolumesize_ARG, UINT64_C(0)) * 2; + + /* If in a VG, must change using volume group. */ + if (*pv->vg_name) { + log_verbose("Finding volume group of physical volume \"%s\"", + pv_name); + + if (!lock_vol(cmd, pv->vg_name, LCK_VG_WRITE)) { + log_error("Can't get lock for %s", pv->vg_name); + return 0; + } + + if (!(vg = vg_read(cmd, pv->vg_name, &consistent))) { + unlock_vg(cmd, pv->vg_name); + log_error("Unable to find volume group of \"%s\"", + pv_name); + return 0; + } + + if (vg->status & EXPORTED_VG) { + unlock_vg(cmd, pv->vg_name); + log_error("Volume group \"%s\" is exported", vg->name); + return 0; + } + + if (!(vg->status & LVM_WRITE)) { + unlock_vg(cmd, pv->vg_name); + log_error("Volume group \"%s\" is read-only", vg->name); + return 0; + } + + if (!(pvl = find_pv_in_vg(vg, pv_name))) { + unlock_vg(cmd, pv->vg_name); + log_error + ("Unable to find \"%s\" in volume group \"%s\"", + pv_name, vg->name); + return 0; + } + pv = pvl->pv; + if (!archive(vg)) + return 0; + } else { + if (!lock_vol(cmd, ORPHAN, LCK_VG_WRITE)) { + log_error("Can't get lock for orphans"); + return 0; + } + + if (!(pv = pv_read(cmd, pv_name, &mdas, §or, 1))) { + unlock_vg(cmd, ORPHAN); + log_error("Unable to read PV \"%s\"", pv_name); + return 0; + } + + } + + /* change size of PV */ + if (!dev_get_size(pv->dev, &pv->size)) { + log_error("%s: Couldn't get size.", pv_name); + return 0; + } + + if (size) { + if (size > pv->size) + log_print("WARNING: %s: Overriding real size. " + "You could lose data.", pv_name); + log_verbose("%s: Pretending size is %" PRIu64 " sectors.", + pv_name, size); + pv->size = size; + } + + if (pv->size < PV_MIN_SIZE) { + log_error("%s: Size must exceed minimum of %ld sectors.", + pv_name, PV_MIN_SIZE); + return 0; + } + + if (pv->size < pv->pe_start) { + log_error("%s: Size must exceed physical extent start of " + "%" PRIu64 " sectors.", pv_name, pv->pe_start); + return 0; + } + + pv->size -= pv->pe_start; + + log_verbose("Resizing volume \"%s\" to %" PRIu64 " sectors.", + pv_name, pv->size); + + if (pv->pe_size) { + new_pe_count = (pv->size - pv->pe_start) / vg->extent_size; + + log_verbose("That makes %" PRIu32 " extents of %" PRIu32 + " sectors each.", new_pe_count, pv->pe_size); + + if (new_pe_count < pv->pe_count) { + 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); + return 0; + } + } + + old_pe_count = pv->pe_count; + if (!pv_resize(pv, vg, new_pe_count)) return 0; + } + + 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 0; + } + backup(vg); + unlock_vg(cmd, pv->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 0; + } + unlock_vg(cmd, ORPHAN); + } + + log_print("Physical volume \"%s\" changed", pv_name); + + return 1; +} + +int pvresize(struct cmd_context *cmd, int argc, char **argv) +{ + int opt = 0; + int done = 0; + int total = 0; + + struct physical_volume *pv; + char *pv_name; + + struct list mdas; + + list_init(&mdas); + + if (!argc) { + log_error("Please give a physical volume path"); + return EINVALID_CMD_LINE; + } + + for (; opt < argc; opt++) { + pv_name = argv[opt]; + /* FIXME Read VG instead - pv_read will fail */ + if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1))) { + log_error("Failed to read physical volume %s", + pv_name); + continue; + } + total++; + done += _pvresize_single(cmd, pv); + } + + log_print("%d physical volume%s changed / %d physical volume%s " + "not changed", + done, done == 1 ? "" : "s", + total - done, (total - done) == 1 ? "" : "s"); + + return (total == done) ? ECMD_PROCESSED : ECMD_FAILED; +} Index: tools/stub.h =================================================================== RCS file: /cvs/lvm2/LVM2/tools/stub.h,v retrieving revision 1.43 diff -b -u -r1.43 stub.h --- tools/stub.h 30 Mar 2004 19:35:43 -0000 1.43 +++ tools/stub.h 28 Oct 2005 20:04:52 -0000 @@ -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.");