1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-03-06 00:58:48 +03:00

Add sparse devices: lvcreate -s --virtualoriginsize (hidden zero origin).

Add lvs origin_size field.
Fix linux configure --enable-debug to exclude -O2.

Still a few rough edges, but hopefully usable now:
  lvcreate -s vg1 -L 100M --virtualoriginsize 1T
This commit is contained in:
Alasdair Kergon 2009-04-25 01:17:59 +00:00
parent 7f54ef36dc
commit 87f42fda5e
15 changed files with 234 additions and 74 deletions

View File

@ -1,5 +1,8 @@
Version 2.02.46 -
================================
Add sparse devices: lvcreate -s --virtualoriginsize (hidden zero origin).
Add lvs origin_size field.
Fix linux configure --enable-debug to exclude -O2.
Implement lvconvert --repair, for repairing partially failed mirrors.
Fix vgreduce --removemissing failure exit code.
Fix remote metadata backup for clvmd.

View File

@ -65,6 +65,7 @@ static struct flag _lv_flags[] = {
{CONVERTING, NULL, 0},
{PARTIAL_LV, NULL, 0},
{POSTORDER_FLAG, NULL, 0},
{VIRTUAL_ORIGIN, NULL, 0},
{0, NULL, 0}
};

View File

@ -77,6 +77,7 @@ struct pv_segment;
//#define POSTORDER_FLAG 0x02000000U /* Not real flags, reserved for
//#define POSTORDER_OPEN_FLAG 0x04000000U temporary use inside vg_read_internal. */
//#define VIRTUAL_ORIGIN 0x08000000U /* LV - internal use only */
#define LVM_READ 0x00000100U /* LV VG */
#define LVM_WRITE 0x00000200U /* LV VG */
@ -531,6 +532,7 @@ struct lv_segment *first_seg(const struct logical_volume *lv);
* Useful functions for managing snapshots.
*/
int lv_is_origin(const struct logical_volume *lv);
int lv_is_virtual_origin(const struct logical_volume *lv);
int lv_is_cow(const struct logical_volume *lv);
int lv_is_visible(const struct logical_volume *lv);

View File

@ -81,6 +81,7 @@
#define POSTORDER_FLAG 0x02000000U /* Not real flags, reserved for */
#define POSTORDER_OPEN_FLAG 0x04000000U /* temporary use inside vg_read_internal. */
#define VIRTUAL_ORIGIN 0x08000000U /* LV - internal use only */
//#define LVM_READ 0x00000100U /* LV VG */
//#define LVM_WRITE 0x00000200U /* LV VG */

View File

@ -44,6 +44,12 @@ int lv_is_displayable(const struct logical_volume *lv)
return (lv->status & VISIBLE_LV) || lv_is_cow(lv) ? 1 : 0;
}
int lv_is_virtual_origin(const struct logical_volume *lv)
{
return (lv->status & VIRTUAL_ORIGIN) ? 1 : 0;
}
/* Given a cow LV, return the snapshot lv_segment that uses it */
struct lv_segment *find_cow(const struct logical_volume *lv)
{
@ -105,6 +111,10 @@ int vg_add_snapshot(const char *name, struct logical_volume *origin,
cow->status &= ~VISIBLE_LV;
/* FIXME Assumes an invisible origin belongs to a sparse device */
if (!lv_is_visible(origin))
origin->status |= VIRTUAL_ORIGIN;
dm_list_add(&origin->snapshot_segs, &seg->origin_list);
return 1;

View File

@ -53,7 +53,7 @@
*/
/* *INDENT-OFF* */
FIELD(LVS, lv, STR, "LV UUID", lvid.id[1], 38, uuid, "lv_uuid", "Unique identifier")
FIELD(LVS, lv, STR, "LV UUID", lvid.id[1], 38, uuid, "lv_uuid", "Unique identifier.")
FIELD(LVS, lv, STR, "LV", lvid, 4, lvname, "lv_name", "Name. LVs created for internal use are enclosed in brackets.")
FIELD(LVS, lv, STR, "Attr", lvid, 4, lvstatus, "lv_attr", "Various attributes - see man page.")
FIELD(LVS, lv, NUM, "Maj", major, 3, int32, "lv_major", "Persistent major number or -1 if not persistent.")
@ -64,11 +64,12 @@ FIELD(LVS, lv, STR, "KMin", lvid, 4, lvkmin, "lv_kernel_minor", "Currently assig
FIELD(LVS, lv, NUM, "KRahead", lvid, 7, lvkreadahead, "lv_kernel_read_ahead", "Currently-in-use read ahead setting in current units.")
FIELD(LVS, lv, NUM, "LSize", size, 5, size64, "lv_size", "Size of LV in current units.")
FIELD(LVS, lv, NUM, "#Seg", lvid, 4, lvsegcount, "seg_count", "Number of segments in LV.")
FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, "origin", "For snapshots, the origin device of this LV")
FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, "origin", "For snapshots, the origin device of this LV.")
FIELD(LVS, lv, NUM, "OSize", lvid, 5, originsize, "origin_size", "For snapshots, the size of the origin device of this LV.")
FIELD(LVS, lv, NUM, "Snap%", lvid, 6, snpercent, "snap_percent", "For snapshots, the percentage full if LV is active.")
FIELD(LVS, lv, NUM, "Copy%", lvid, 6, copypercent, "copy_percent", "For mirrors and pvmove, current percentage in-sync.")
FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, "move_pv", "For pvmove, Source PV of temporary LV created by pvmove")
FIELD(LVS, lv, STR, "Convert", lvid, 7, convertlv, "convert_lv", "For lvconvert, Name of temporary LV created by lvconvert")
FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, "move_pv", "For pvmove, Source PV of temporary LV created by pvmove.")
FIELD(LVS, lv, STR, "Convert", lvid, 7, convertlv, "convert_lv", "For lvconvert, Name of temporary LV created by lvconvert.")
FIELD(LVS, lv, STR, "LV Tags", tags, 7, tags, "lv_tags", "Tags, if any.")
FIELD(LVS, lv, STR, "Log", lvid, 3, loglv, "mirror_log", "For mirrors, the LV holding the synchronisation log.")
FIELD(LVS, lv, STR, "Modules", lvid, 7, modules, "modules", "Kernel device-mapper modules required for this LV.")
@ -111,7 +112,7 @@ FIELD(VGS, vg, NUM, "#VMda", cmd, 5, vgmdas, "vg_mda_count", "Number of metadata
FIELD(VGS, vg, NUM, "VMdaFree", cmd, 9, vgmdafree, "vg_mda_free", "Free metadata area space for this VG in current units.")
FIELD(VGS, vg, NUM, "VMdaSize", cmd, 9, vgmdasize, "vg_mda_size", "Size of smallest metadata area for this VG in current units.")
FIELD(SEGS, seg, STR, "Type", list, 4, segtype, "segtype", "Type of LV segment")
FIELD(SEGS, seg, STR, "Type", list, 4, segtype, "segtype", "Type of LV segment.")
FIELD(SEGS, seg, NUM, "#Str", area_count, 4, uint32, "stripes", "Number of stripes or mirror legs.")
FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, "stripesize", "For stripes, amount of data placed on one device before switching to the next.")
FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, "stripe_size", "For stripes, amount of data placed on one device before switching to the next.")

View File

@ -471,20 +471,6 @@ static int _segtype_disp(struct dm_report *rh __attribute((unused)),
return 1;
}
static int _origin_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute((unused)))
{
const struct logical_volume *lv = (const struct logical_volume *) data;
if (lv_is_cow(lv))
return dm_report_field_string(rh, field,
(const char **) &origin_from_cow(lv)->name);
dm_report_field_set_value(field, "", NULL);
return 1;
}
static int _loglv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute((unused)))
@ -537,6 +523,19 @@ static int _lvname_disp(struct dm_report *rh, struct dm_pool *mem,
return 1;
}
static int _origin_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
if (lv_is_cow(lv))
return _lvname_disp(rh, mem, field, origin_from_cow(lv), private);
dm_report_field_set_value(field, "", NULL);
return 1;
}
static int _movepv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
struct dm_report_field *field,
const void *data, void *private __attribute((unused)))
@ -723,7 +722,24 @@ static int _chunksize_disp(struct dm_report *rh, struct dm_pool *mem,
if (lv_is_cow(seg->lv))
size = (uint64_t) find_cow(seg->lv)->chunk_size;
else
size = 0;
size = UINT64_C(0);
return _size64_disp(rh, mem, field, &size, private);
}
static int _originsize_disp(struct dm_report *rh, struct dm_pool *mem,
struct dm_report_field *field,
const void *data, void *private)
{
const struct logical_volume *lv = (const struct logical_volume *) data;
uint64_t size;
if (lv_is_cow(lv))
size = (uint64_t) find_cow(lv)->len * lv->vg->extent_size;
else if (lv_is_origin(lv))
size = lv->size;
else
size = UINT64_C(0);
return _size64_disp(rh, mem, field, &size, private);
}

View File

@ -25,7 +25,9 @@ VolumeGroupName [PhysicalVolumePath...]
{\-l|\-\-extents LogicalExtentsNumber[%{VG|FREE}] |
\-L|\-\-size LogicalVolumeSize[kKmMgGtT]}
[\-c|\-\-chunksize ChunkSize]
\-s|\-\-snapshot \-n|\-\-name SnapshotLogicalVolumeName OriginalLogicalVolumePath
\-n|\-\-name SnapshotLogicalVolumeName
\-s|\-\-snapshot
[OriginalLogicalVolumePath | VolumeGroupName \-\-virtualoriginsize VirtualOriginSize]
.SH DESCRIPTION
lvcreate creates a new logical volume in a volume group ( see
.B vgcreate(8), vgchange(8)
@ -144,6 +146,19 @@ to grow it. Shrinking a snapshot is supported by
as well. Run
.B lvdisplay(8)
on the snapshot in order to check how much data is allocated to it.
Note that a small amount of the space you allocate to the snapshot is
used to track the locations of the chunks of data, so you should
allocate slightly more space than you actually need and monitor the
rate at which the snapshot data is growing so you can avoid running out
of space.
.TP
.I \-\-virtualoriginsize VirtualOriginSize
In conjunction with \-\-snapshot, create a sparse device of the given size
(in MB by default). Anything written to the device will be returned when
reading from it. Reading from other areas of the device will return
blocks of zeros. It is implemented by creating a hidden virtual device of the
requested size using the zero target. A suffix of _vorigin is used for
this device.
.TP
.I \-Z, \-\-zero y|n
Controls zeroing of the first KB of data in the new logical volume.
@ -180,6 +195,11 @@ contains a file system, you can mount the snapshot logical volume on an
arbitrary directory in order to access the contents of the filesystem to run
a backup while the original filesystem continues to get updated.
"lvcreate --virtualoriginsize 1T --size 100M --snapshot --name sparse vg1"
.br
creates a sparse device named /dev/vg1/sparse of size 1TB with space for just
under 100MB of actual data on it.
.SH SEE ALSO
.BR lvm (8),
.BR vgcreate (8),

View File

@ -39,7 +39,7 @@ Comma-separated ordered list of columns. Precede the list with '+' to append
to the default selection of columns instead of replacing it. Column names are:
lv_uuid, lv_name, lv_attr, lv_major, lv_minor, lv_kernel_major, lv_kernel_minor,
lv_size, seg_count, origin, snap_percent,
copy_percent, move_pv, lv_tags,
copy_percent, move_pv, lv_tags, origin_size,
segtype, stripes,
stripesize, chunksize, seg_start, seg_size, seg_tags, devices,
regionsize, mirror_log, modules.

View File

@ -58,6 +58,7 @@ arg(nameprefixes_ARG, '\0', "nameprefixes", NULL, 0)
arg(unquoted_ARG, '\0', "unquoted", NULL, 0)
arg(rows_ARG, '\0', "rows", NULL, 0)
arg(dataalignment_ARG, '\0', "dataalignment", size_kb_arg, 0)
arg(virtualoriginsize_ARG, '\0', "virtualoriginsize", size_mb_arg, 0)
/* Allow some variations */
arg(resizable_ARG, '\0', "resizable", yes_no_arg, 0)

View File

@ -163,13 +163,15 @@ xx(lvcreate,
"\t[-t|--test]\n"
"\t[-v|--verbose]\n"
"\t[--version]\n"
"\tOriginalLogicalVolume[Path] [PhysicalVolumePath...]\n\n",
"\t[OriginalLogicalVolume[Path] |\n"
"\t VolumeGroupName[Path] --virtualoriginsize VirtualOriginSize]]\n"
"\t[PhysicalVolumePath...]\n\n",
addtag_ARG, alloc_ARG, autobackup_ARG, chunksize_ARG, contiguous_ARG,
corelog_ARG, extents_ARG, major_ARG, minor_ARG, mirrorlog_ARG, mirrors_ARG,
name_ARG, nosync_ARG, permission_ARG, persistent_ARG, readahead_ARG,
regionsize_ARG, size_ARG, snapshot_ARG, stripes_ARG, stripesize_ARG,
test_ARG, type_ARG, zero_ARG)
test_ARG, type_ARG, virtualoriginsize_ARG, zero_ARG)
xx(lvdisplay,
"Display information about a logical volume",

View File

@ -559,7 +559,8 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv,
return ECMD_FAILED;
}
if (lv_is_cow(lv)) {
if (lv_is_cow(lv) && !lv_is_virtual_origin(origin_from_cow(lv)) &&
arg_count(cmd, available_ARG)) {
log_error("Can't change snapshot logical volume \"%s\"",
lv->name);
return ECMD_FAILED;

View File

@ -43,6 +43,8 @@ struct lvcreate_params {
/* size */
uint32_t extents;
uint64_t size;
uint32_t voriginextents;
uint64_t voriginsize;
percent_t percent;
uint32_t permission;
@ -64,7 +66,7 @@ static int _lvcreate_name_params(struct lvcreate_params *lp,
if (arg_count(cmd, name_ARG))
lp->lv_name = arg_value(cmd, name_ARG);
if (lp->snapshot) {
if (lp->snapshot && !arg_count(cmd, virtualoriginsize_ARG)) {
if (!argc) {
log_err("Please specify a logical volume to act as "
"the snapshot origin.");
@ -175,6 +177,20 @@ static int _read_size_params(struct lvcreate_params *lp,
lp->percent = PERCENT_NONE;
}
/* Size returned in kilobyte units; held in sectors */
if (arg_count(cmd, virtualoriginsize_ARG)) {
if (arg_sign_value(cmd, virtualoriginsize_ARG, 0) == SIGN_MINUS) {
log_error("Negative virtual origin size is invalid");
return 0;
}
lp->voriginsize = arg_uint64_value(cmd, virtualoriginsize_ARG,
UINT64_C(0));
if (!lp->voriginsize) {
log_error("Virtual origin size may not be zero");
return 0;
}
}
return 1;
}
@ -390,6 +406,10 @@ static int _lvcreate_params(struct lvcreate_params *lp, struct cmd_context *cmd,
log_error("-c is only available with snapshots");
return 0;
}
if (arg_count(cmd, virtualoriginsize_ARG)) {
log_error("--virtualoriginsize is only available with snapshots");
return 0;
}
}
if (lp->mirrors > 1) {
@ -511,12 +531,73 @@ static int _lvcreate_params(struct lvcreate_params *lp, struct cmd_context *cmd,
return 1;
}
static uint64_t _extents_from_size(struct cmd_context *cmd, uint64_t size,
uint32_t extent_size)
{
if (size % extent_size) {
size += extent_size - size % extent_size;
log_print("Rounding up size to full physical extent %s",
display_size(cmd, size));
}
if (size > (uint64_t) UINT32_MAX * extent_size) {
log_error("Volume too large (%s) for extent size %s. "
"Upper limit is %s.",
display_size(cmd, size),
display_size(cmd, (uint64_t) extent_size),
display_size(cmd, (uint64_t) UINT32_MAX *
extent_size));
return 0;
}
return (uint64_t) size / extent_size;
}
static struct logical_volume *_create_virtual_origin(struct cmd_context *cmd,
struct volume_group *vg,
const char *lv_name,
uint32_t permission,
uint64_t voriginextents)
{
const struct segment_type *segtype;
size_t len;
char *vorigin_name;
struct logical_volume *lv;
if (!(segtype = get_segtype_from_string(cmd, "zero"))) {
log_error("Zero segment type for virtual origin not found");
return 0;
}
len = strlen(lv_name) + 32;
if (!(vorigin_name = alloca(len)) ||
dm_snprintf(vorigin_name, len, "%s_vorigin", lv_name) < 0) {
log_error("Virtual origin name allocation failed.");
return 0;
}
if (!(lv = lv_create_empty(vorigin_name, NULL, permission,
ALLOC_INHERIT, 0, vg)))
return_0;
if (!lv_extend(lv, segtype, 1, 0, 1, voriginextents, NULL, 0u, 0u,
NULL, ALLOC_INHERIT))
return_0;
/* store vg on disk(s) */
if (!vg_write(vg) || !vg_commit(vg))
return_0;
backup(vg);
return lv;
}
static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
struct lvcreate_params *lp)
{
uint32_t size_rest;
uint32_t status = 0;
uint64_t tmp_size;
struct logical_volume *lv, *org = NULL;
struct dm_list *pvh;
const char *tag = NULL;
@ -562,28 +643,14 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
return 0;
}
if (lp->size) {
/* No of 512-byte sectors */
tmp_size = lp->size;
if (lp->size &&
!(lp->extents = _extents_from_size(cmd, lp->size, vg->extent_size)))
return_0;
if (tmp_size % vg->extent_size) {
tmp_size += vg->extent_size - tmp_size %
vg->extent_size;
log_print("Rounding up size to full physical extent %s",
display_size(cmd, tmp_size));
}
if (tmp_size > (uint64_t) UINT32_MAX * vg->extent_size) {
log_error("Volume too large (%s) for extent size %s. "
"Upper limit is %s.",
display_size(cmd, tmp_size),
display_size(cmd, (uint64_t) vg->extent_size),
display_size(cmd, (uint64_t) UINT32_MAX *
vg->extent_size));
return 0;
}
lp->extents = (uint64_t) tmp_size / vg->extent_size;
}
if (lp->voriginsize &&
!(lp->voriginextents = _extents_from_size(cmd, lp->voriginsize,
vg->extent_size)))
return_0;
/*
* Create the pv list.
@ -645,37 +712,49 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
log_error("Clustered snapshots are not yet supported.");
return 0;
}
if (!(org = find_lv(vg, lp->origin))) {
log_err("Couldn't find origin volume '%s'.",
lp->origin);
return 0;
}
if (lv_is_cow(org)) {
log_error("Snapshots of snapshots are not supported "
"yet.");
return 0;
}
if (org->status & LOCKED) {
log_error("Snapshots of locked devices are not "
"supported yet");
return 0;
}
if (org->status & MIRROR_IMAGE ||
org->status & MIRROR_LOG ||
org->status & MIRRORED) {
log_error("Snapshots and mirrors may not yet be mixed.");
return 0;
}
/* Must zero cow */
status |= LVM_WRITE;
if (!lv_info(cmd, org, &info, 0, 0)) {
log_error("Check for existence of snapshot origin "
"'%s' failed.", org->name);
return 0;
if (arg_count(cmd, virtualoriginsize_ARG))
origin_active = 1;
else {
if (!(org = find_lv(vg, lp->origin))) {
log_error("Couldn't find origin volume '%s'.",
lp->origin);
return 0;
}
if (lv_is_virtual_origin(lv)) {
log_error("Can't share virtual origins. "
"Use --virtualoriginsize.");
return 0;
}
if (lv_is_cow(org)) {
log_error("Snapshots of snapshots are not "
"supported yet.");
return 0;
}
if (org->status & LOCKED) {
log_error("Snapshots of locked devices are not "
"supported yet");
return 0;
}
if (org->status & MIRROR_IMAGE ||
org->status & MIRROR_LOG ||
org->status & MIRRORED) {
log_error("Snapshots and mirrors may not yet "
"be mixed.");
return 0;
}
if (!lv_info(cmd, org, &info, 0, 0)) {
log_error("Check for existence of snapshot "
"origin '%s' failed.", org->name);
return 0;
}
origin_active = info.exists;
}
origin_active = info.exists;
}
if (!lp->extents) {
@ -828,6 +907,15 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
return 0;
}
if (lp->voriginsize &&
!(org = _create_virtual_origin(cmd, vg, lv->name,
lp->permission,
lp->voriginextents))) {
log_error("Couldn't create virtual origin for LV %s",
lv->name);
goto deactivate_and_revert_new_lv;
}
/* cow LV remains active and becomes snapshot LV */
if (!vg_add_snapshot(NULL, org, lv, NULL,

View File

@ -18,6 +18,14 @@
static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
void *handle __attribute((unused)))
{
struct logical_volume *origin;
/*
* If this is a sparse device, remove its origin too.
*/
if (lv_is_cow(lv) && lv_is_virtual_origin(origin = origin_from_cow(lv)))
lv = origin;
if (!lv_remove_with_dependencies(cmd, lv, arg_count(cmd, force_ARG)))
return ECMD_FAILED;

View File

@ -1229,6 +1229,12 @@ int apply_lvname_restrictions(const char *name)
return 0;
}
if (strstr(name, "_vorigin")) {
log_error("Names including \"_vorigin\" are reserved. "
"Please choose a different LV name.");
return 0;
}
return 1;
}