mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
thin: lvconvert support for external origin
Add basic support for converting LV into an external origin volume. Syntax: lvconvert --thinpool vg/pool --originname renamed_origin -T origin It will convert volume 'origin' into a thin volume, which will use 'renamed_origin' as an external read-only origin. All read/write into origin will go via 'pool'. renamed_origin volume is read-only volume, that could be activated only in read-only mode, and cannot be modified.
This commit is contained in:
parent
2cba0ea9f9
commit
b73de73151
@ -1,5 +1,6 @@
|
||||
Version 2.02.99 -
|
||||
===================================
|
||||
Initial support for lvconvert of thin external origin.
|
||||
Add _lv_remove_segs_using_this_lv() for removal of dependent lvs.
|
||||
Improve activation code for better support of stacked devices.
|
||||
Add _add_layer_target_to_dtree() for adding linear layer into dtree.
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@ -1580,6 +1580,10 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
|
||||
(lv_is_origin(lv_pre) || lv_is_cow(lv_pre)))
|
||||
lockfs = 1;
|
||||
|
||||
/* Converting non-thin LV to thin external origin ? */
|
||||
if (!lv_is_thin_volume(lv) && lv_is_thin_volume(lv_pre))
|
||||
lockfs = 1; /* Sync before conversion */
|
||||
|
||||
if (laopts->origin_only && lv_is_thin_volume(lv) && lv_is_thin_volume(lv_pre))
|
||||
lockfs = 1;
|
||||
|
||||
|
@ -99,6 +99,10 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot
|
||||
.RB [ \-h | \-? | \-\-help ]
|
||||
.RB [ \-v | \-\-verbose ]
|
||||
.RB [ \-\-version ]
|
||||
.RB [ \-T | \-\-thin
|
||||
.IR ExternalOriginLogicalVolume { Name | Path }
|
||||
.RB [ \-\-originname
|
||||
.IR NewExternalOriginVolumeName ]]
|
||||
.RI [ PhysicalVolume [ Path ][ :PE [ -PE ]]...]
|
||||
.sp
|
||||
|
||||
@ -230,6 +234,13 @@ merge finishes, the merged snapshot is removed. Multiple snapshots may
|
||||
be specified on the commandline or a @tag may be used to specify
|
||||
multiple snapshots be merged to their respective origin.
|
||||
.TP
|
||||
.B \-\-originname \fINewExternalOriginVolumeName\fP
|
||||
The name for converted external origin volume.
|
||||
.br
|
||||
Without this option a default names of "lvol#" will be generated where
|
||||
# is the LVM internal number of the logical volume.
|
||||
Converted volume will be read-only.
|
||||
.TP
|
||||
.BR \-\-poolmetadata " " \fIThinPoolMetadataLogicalVolume { \fIName | \fIPath }
|
||||
Specifies thin pool metadata logical volume.
|
||||
The size should be in between 2MiB and 16GiB.
|
||||
@ -288,6 +299,13 @@ StripeSize must be 2^n (n = 2 to 9) for metadata in LVM1 format.
|
||||
For metadata in LVM2 format, the stripe size may be a larger
|
||||
power of 2 but must not exceed the physical extent size.
|
||||
.TP
|
||||
.IR \fB\-T ", " \fB\-\-thin " " ExternalOriginLogicalVolume { Name | Path }
|
||||
Changes the logical volume into a thin volume for the thin pool
|
||||
specified with the option \fB\-\-thinpool\fP. \fIExternalOriginLogicalVolume\fP
|
||||
is converted into a new read-only logical volume which will be used as an
|
||||
external origin volume for unprovisioned areas.
|
||||
The non-default name for this new volume can be specified with \fB\-\-originname\fP.
|
||||
.TP
|
||||
.IR \fB\-\-thinpool " " ThinPoolLogicalVolume { Name | Path }
|
||||
Changes logical volume into a thin pool volume. The volume
|
||||
will store the pool's data.
|
||||
@ -379,6 +397,20 @@ available in the volume group.
|
||||
.sp
|
||||
.B lvconvert \-\-replace /dev/sdb1 vg00/my_raid1 /dev/sdf1
|
||||
|
||||
Convert the logical volume "vg00/lvpool" into a thin pool with chunk size 128KiB
|
||||
and convert "vg00/lv1" into a thin volume using this pool. Original "vg00/lv1"
|
||||
is used as an external read-only origin, where all writes to such volume
|
||||
are stored in the "vg00/lvpool".
|
||||
.sp
|
||||
.B lvconvert \-\-thinpool vg00/lvpool -c 128 -T lv1
|
||||
|
||||
Convert the logical volume "vg00/origin" into a thin volume from the thin pool
|
||||
"vg00/lvpool". This thin volume will use "vg00/origin" as an external origin
|
||||
volume for unprovisioned areas in this volume.
|
||||
For the read-only external origin use the new name "vg00/external".
|
||||
.sp
|
||||
.B lvconvert \-\-thinpool vg00/lvpool \-\-originname external -T vg00/origin
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR lvm (8),
|
||||
.BR vgcreate (8),
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@ -69,6 +69,7 @@ arg(dataalignment_ARG, '\0', "dataalignment", size_kb_arg, 0)
|
||||
arg(dataalignmentoffset_ARG, '\0', "dataalignmentoffset", size_kb_arg, 0)
|
||||
arg(virtualoriginsize_ARG, '\0', "virtualoriginsize", size_mb_arg, 0)
|
||||
arg(noudevsync_ARG, '\0', "noudevsync", NULL, 0)
|
||||
arg(originname_ARG, '\0', "originname", string_arg, 0)
|
||||
arg(poll_ARG, '\0', "poll", yes_no_arg, 0)
|
||||
arg(poolmetadata_ARG, '\0', "poolmetadata", string_arg, 0)
|
||||
arg(poolmetadatasize_ARG, '\0', "poolmetadatasize", size_mb_arg, 0)
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@ -151,6 +151,8 @@ xx(lvconvert,
|
||||
"\t [--poolmetadatasize size]\n"
|
||||
"\t [-r|--readahead ReadAheadSectors|auto|none]\n"
|
||||
"\t [--stripes Stripes [-I|--stripesize StripeSize]]]\n"
|
||||
"\t[-T|--thin ExternalLogicalVolume[Path]\n"
|
||||
"\t [--originname NewExternalOriginVolumeName]]\n"
|
||||
"\t[-Z|--zero {y|n}]\n"
|
||||
"\t[-d|--debug] [-h|-?|--help] [-v|--verbose]\n",
|
||||
|
||||
@ -158,7 +160,8 @@ xx(lvconvert,
|
||||
merge_ARG, mirrorlog_ARG, mirrors_ARG, name_ARG, noudevsync_ARG,
|
||||
readahead_ARG, regionsize_ARG, repair_ARG, replace_ARG, snapshot_ARG, splitmirrors_ARG,
|
||||
trackchanges_ARG, type_ARG, stripes_long_ARG, stripesize_ARG, test_ARG,
|
||||
chunksize_ARG, discards_ARG, poolmetadata_ARG, poolmetadatasize_ARG, thinpool_ARG,
|
||||
chunksize_ARG, discards_ARG, poolmetadata_ARG, poolmetadatasize_ARG,
|
||||
originname_ARG, thin_ARG, thinpool_ARG,
|
||||
use_policies_ARG, yes_ARG, force_ARG, zero_ARG)
|
||||
|
||||
xx(lvcreate,
|
||||
|
@ -57,6 +57,7 @@ struct lvconvert_params {
|
||||
struct logical_volume *lv_to_poll;
|
||||
|
||||
uint64_t poolmetadata_size;
|
||||
const char *origin_lv_name;
|
||||
const char *pool_data_lv_name;
|
||||
const char *pool_metadata_lv_name;
|
||||
thin_discards_t discards;
|
||||
@ -68,6 +69,7 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
|
||||
{
|
||||
char *ptr;
|
||||
const char *vg_name = NULL;
|
||||
const char *tmp_str;
|
||||
|
||||
if (lp->merge)
|
||||
return 1;
|
||||
@ -94,16 +96,35 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
|
||||
|
||||
if (lp->pool_data_lv_name) {
|
||||
if (*pargc) {
|
||||
log_error("More then one logical volume name name specified.");
|
||||
return 0;
|
||||
}
|
||||
if (!arg_count(cmd, thin_ARG)) {
|
||||
log_error("More then one logical volume name specified.");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (arg_count(cmd, thin_ARG)) {
|
||||
log_error("External thin volume name is missing.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lp->vg_name || !validate_name(lp->vg_name)) {
|
||||
log_error("Please provide a valid volume group name.");
|
||||
return 0;
|
||||
if (!lp->vg_name || !validate_name(lp->vg_name)) {
|
||||
log_error("Please provide a valid volume group name.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lp->lv_name = lp->pool_data_lv_name;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->origin_lv_name) {
|
||||
/* FIXME: Using generic routine */
|
||||
if (strchr(lp->origin_lv_name, '/')) {
|
||||
if (!(lp->vg_name = extract_vgname(cmd, lp->origin_lv_name)))
|
||||
return_0;
|
||||
/* Strip VG from origin_lv_name */
|
||||
if ((tmp_str = strrchr(lp->origin_lv_name, '/')))
|
||||
lp->origin_lv_name = tmp_str + 1;
|
||||
}
|
||||
lp->lv_name = lp->pool_data_lv_name;
|
||||
return 1; /* Create metadata LV on it's own */
|
||||
}
|
||||
|
||||
if (!*pargc) {
|
||||
@ -219,6 +240,9 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
lp->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN);
|
||||
} else if (arg_count(cmd, thin_ARG)) {
|
||||
log_error("--thin is only valid with --thinpool.");
|
||||
return 0;
|
||||
} else if (arg_count(cmd, discards_ARG)) {
|
||||
log_error("--discards is only valid with --thinpool.");
|
||||
return 0;
|
||||
@ -376,6 +400,13 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
|
||||
lp->pool_data_lv_name = tmp_str + 1;
|
||||
}
|
||||
|
||||
if (arg_count(cmd, originname_ARG)) {
|
||||
if (!(lp->origin_lv_name = arg_str_value(cmd, originname_ARG, NULL))) {
|
||||
log_error("--originname is invalid.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, "thin-pool"));
|
||||
if (!lp->segtype)
|
||||
return_0;
|
||||
@ -1825,6 +1856,116 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Swap lvid and LV names */
|
||||
static int _swap_lv(struct cmd_context *cmd,
|
||||
struct logical_volume *a, struct logical_volume *b)
|
||||
{
|
||||
union lvid lvid;
|
||||
const char *name;
|
||||
|
||||
lvid = a->lvid;
|
||||
a->lvid = b->lvid;
|
||||
b->lvid = lvid;
|
||||
|
||||
name = a->name;
|
||||
a->name = b->name;
|
||||
if (!lv_rename_update(cmd, b, name, 0))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _lvconvert_thinpool_external(struct cmd_context *cmd,
|
||||
struct logical_volume *pool_lv,
|
||||
struct logical_volume *external_lv,
|
||||
struct lvconvert_params *lp)
|
||||
{
|
||||
struct logical_volume *torigin_lv;
|
||||
struct volume_group *vg = pool_lv->vg;
|
||||
struct lvcreate_params lvc = { 0 };
|
||||
|
||||
dm_list_init(&lvc.tags);
|
||||
|
||||
if (!(lvc.segtype = get_segtype_from_string(cmd, "thin")))
|
||||
return_0;
|
||||
|
||||
lvc.activate = CHANGE_AE;
|
||||
lvc.alloc = ALLOC_INHERIT;
|
||||
lvc.lv_name = lp->origin_lv_name;
|
||||
lvc.major = -1;
|
||||
lvc.minor = -1;
|
||||
lvc.permission = LVM_READ;
|
||||
lvc.pool = pool_lv->name;
|
||||
lvc.pvh = &vg->pvs;
|
||||
lvc.read_ahead = DM_READ_AHEAD_AUTO;
|
||||
lvc.stripes = 1;
|
||||
lvc.vg_name = vg->name;
|
||||
lvc.voriginextents = external_lv->le_count;
|
||||
lvc.voriginsize = external_lv->size;
|
||||
|
||||
/* New thin LV needs to be created (all messages sent to pool) */
|
||||
if (!(torigin_lv = lv_create_single(vg, &lvc)))
|
||||
return_0;
|
||||
|
||||
/* Activate again via -torigin, so this active LV is not needed */
|
||||
if (!deactivate_lv(cmd, torigin_lv)) {
|
||||
log_error("Aborting. Unable to deactivate new LV. "
|
||||
"Manual intervention required.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Crashing till this point will leave plain thin volume
|
||||
* which could be easily removed by the user after i.e. power-off
|
||||
*/
|
||||
|
||||
if (!_swap_lv(cmd, torigin_lv, external_lv)) {
|
||||
stack;
|
||||
goto revert_new_lv;
|
||||
}
|
||||
|
||||
/* Preserve read-write status of original LV here */
|
||||
torigin_lv->status |= (external_lv->status & LVM_WRITE);
|
||||
|
||||
if (!attach_thin_external_origin(first_seg(torigin_lv), external_lv)) {
|
||||
stack;
|
||||
goto revert_new_lv;
|
||||
}
|
||||
|
||||
if (!_reload_lv(cmd, vg, torigin_lv)) {
|
||||
stack;
|
||||
goto deactivate_and_revert_new_lv;
|
||||
}
|
||||
|
||||
log_print_unless_silent("Converted %s/%s to thin external origin.",
|
||||
vg->name, external_lv->name);
|
||||
|
||||
return 1;
|
||||
|
||||
deactivate_and_revert_new_lv:
|
||||
if (!_swap_lv(cmd, torigin_lv, external_lv))
|
||||
stack;
|
||||
|
||||
if (!deactivate_lv(cmd, torigin_lv)) {
|
||||
log_error("Unable to deactivate failed new LV. "
|
||||
"Manual intervention required.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!detach_thin_external_origin(first_seg(torigin_lv)))
|
||||
return_0;
|
||||
|
||||
revert_new_lv:
|
||||
/* FIXME Better to revert to backup of metadata? */
|
||||
if (!lv_remove(torigin_lv) || !vg_write(vg) || !vg_commit(vg))
|
||||
log_error("Manual intervention may be required to remove "
|
||||
"abandoned LV(s) before retrying.");
|
||||
else
|
||||
backup(vg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Thin lvconvert version which
|
||||
* rename metadata
|
||||
@ -1843,6 +1984,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd,
|
||||
struct logical_volume *data_lv;
|
||||
struct logical_volume *metadata_lv;
|
||||
struct logical_volume *pool_metadata_lv;
|
||||
struct logical_volume *external_lv = NULL;
|
||||
|
||||
if (!lv_is_visible(pool_lv)) {
|
||||
log_error("Can't convert internal LV %s/%s.",
|
||||
@ -1850,6 +1992,19 @@ static int _lvconvert_thinpool(struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg_count(cmd, thin_ARG)) {
|
||||
external_lv = pool_lv;
|
||||
if (!(pool_lv = find_lv(external_lv->vg, lp->pool_data_lv_name))) {
|
||||
log_error("Can't find pool LV %s/%s.",
|
||||
external_lv->vg->name, lp->pool_data_lv_name);
|
||||
return 0;
|
||||
}
|
||||
if (lv_is_thin_pool(pool_lv)) {
|
||||
r = 1; /* Already existing thin pool */
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (lv_is_thin_type(pool_lv) && !lp->pool_metadata_lv_name) {
|
||||
log_error("Can't use thin logical volume %s/%s for thin pool data.",
|
||||
pool_lv->vg->name, pool_lv->name);
|
||||
@ -2064,7 +2219,12 @@ mda_write:
|
||||
|
||||
r = 1;
|
||||
out:
|
||||
if (r && external_lv &&
|
||||
!(r = _lvconvert_thinpool_external(cmd, pool_lv, external_lv, lp)))
|
||||
stack;
|
||||
|
||||
backup(pool_lv->vg);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user