summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2022-09-09 16:07:07 -0500
committerDavid Teigland <teigland@redhat.com>2022-09-09 16:18:55 -0500
commit18722dfdf4d3e6f172d0b2af8bbdc4a154ea1dc0 (patch)
treed6e85ac7c07e5d225372f1e169540e0cc6d0fdf5
parent55e9494e5fe3e3d2790fca6330b848a577e0b1c0 (diff)
downloadlvm2-18722dfdf4d3e6f172d0b2af8bbdc4a154ea1dc0.tar.gz
lvresize: restructure code
Rewrite top level resize function to prepare for adding new fs resizing.
-rw-r--r--lib/commands/cmd_enum.h19
-rw-r--r--lib/commands/toolcontext.h2
-rw-r--r--lib/locking/lvmlockd.c6
-rw-r--r--lib/metadata/lv_manip.c942
-rw-r--r--lib/metadata/metadata-exported.h39
-rw-r--r--lib/metadata/metadata.c12
-rw-r--r--lib/metadata/pool_manip.c3
-rw-r--r--test/shell/lvextend-thin-full.sh11
-rw-r--r--test/shell/lvresize-full.sh13
-rw-r--r--tools/command-lines.in20
-rw-r--r--tools/lvextend.c2
-rw-r--r--tools/lvmcmdline.c13
-rw-r--r--tools/lvreduce.c2
-rw-r--r--tools/lvresize.c346
-rw-r--r--tools/tools.h16
15 files changed, 889 insertions, 557 deletions
diff --git a/lib/commands/cmd_enum.h b/lib/commands/cmd_enum.h
new file mode 100644
index 000000000..939af81c3
--- /dev/null
+++ b/lib/commands/cmd_enum.h
@@ -0,0 +1,19 @@
+#ifndef _CMD_ENUM_H
+#define _CMD_ENUM_H
+
+/*
+ * tools/cmds.h is generated by the Makefile. For each command definition
+ * in command-lines.in, cmds.h contains:
+ * cmd(foo_CMD, foo)
+ *
+ * This header adds each of the foo_CMD's into an enum, so there's
+ * a unique integer identifier for each command definition.
+ */
+
+enum {
+#define cmd(a, b) a ,
+#include "../tools/cmds.h"
+#undef cmd
+};
+
+#endif
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index 7a4979b33..eb2d1922b 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -18,6 +18,7 @@
#include "lib/device/dev-cache.h"
#include "lib/device/dev-type.h"
+#include "lib/commands/cmd_enum.h"
#include <limits.h>
@@ -94,6 +95,7 @@ struct cmd_context {
const char *name; /* needed before cmd->command is set */
struct command_name *cname;
struct command *command;
+ int command_enum; /* duplicate from command->command_enum for lib code */
char **argv;
struct arg_values *opt_arg_values;
struct dm_list arg_value_groups;
diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c
index f0183db3d..2ef0900d4 100644
--- a/lib/locking/lvmlockd.c
+++ b/lib/locking/lvmlockd.c
@@ -577,8 +577,9 @@ static int _extend_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg,
(uint32_t)(new_size_sectors * SECTOR_SIZE));
lp.size = new_size_sectors;
+ lp.pvh = &vg->pvs;
- if (!lv_resize(lv, &lp, &vg->pvs)) {
+ if (!lv_resize(cmd, lv, &lp)) {
log_error("Extend sanlock LV %s to size %s failed.",
display_lvname(lv), display_size(cmd, lp.size));
return 0;
@@ -2733,6 +2734,9 @@ int lockd_lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
if (!_lvmlockd_connected)
return 0;
+ if (lv_is_lockd_sanlock_lv(lv))
+ return 1;
+
/*
* A special case for gfs2 where we want to allow lvextend
* of an LV that has an existing shared lock, which is normally
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index a9057830d..e819db8d5 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -4935,8 +4935,8 @@ static int _validate_stripesize(const struct volume_group *vg,
return 1;
}
-static int _request_confirmation(const struct logical_volume *lv,
- const struct lvresize_params *lp)
+static int _lv_reduce_confirmation(struct logical_volume *lv,
+ struct lvresize_params *lp)
{
const struct volume_group *vg = lv->vg;
struct lvinfo info = { 0 };
@@ -4946,15 +4946,7 @@ static int _request_confirmation(const struct logical_volume *lv,
return 0;
}
- if (lp->resizefs) {
- if (!info.exists) {
- log_error("Logical volume %s must be activated "
- "before resizing filesystem.",
- display_lvname(lv));
- return 0;
- }
- return 1;
- } else if (lv_is_vdo(lv) && !info.exists) {
+ if (lv_is_vdo(lv) && !info.exists) {
log_error("Logical volume %s must be activated "
"before reducing device size.",
display_lvname(lv));
@@ -5064,8 +5056,9 @@ static uint32_t _adjust_amount(dm_percent_t percent, int policy_threshold, int p
return (policy_amount < percent) ? (uint32_t) percent : (uint32_t) policy_amount;
}
-static int _lvresize_adjust_policy(const struct logical_volume *lv,
- uint32_t *amount, uint32_t *meta_amount)
+/* "amount" here is percent */
+int lv_extend_policy_calculate_percent(struct logical_volume *lv,
+ uint32_t *amount, uint32_t *meta_amount)
{
struct cmd_context *cmd = lv->vg->cmd;
dm_percent_t percent;
@@ -5111,8 +5104,10 @@ static int _lvresize_adjust_policy(const struct logical_volume *lv,
}
}
- if (policy_threshold >= 100)
+ if (policy_threshold >= 100) {
+ log_debug("lvextend policy disabled by threshold 100");
return 1; /* nothing to do */
+ }
if (!policy_amount) {
log_error("Can't extend %s with %s autoextend percent set to 0%%.",
@@ -5148,6 +5143,8 @@ static int _lvresize_adjust_policy(const struct logical_volume *lv,
*amount = _adjust_amount(percent, policy_threshold, policy_amount);
+ log_debug("lvextend policy calculated percentages main %u meta %u from threshold %d percent %d",
+ *amount, *meta_amount, policy_threshold, policy_amount);
return 1;
}
@@ -5184,120 +5181,6 @@ static uint32_t _lvseg_get_stripes(struct lv_segment *seg, uint32_t *stripesize)
return 0;
}
-static int _lvresize_check(struct logical_volume *lv,
- struct lvresize_params *lp)
-{
- struct volume_group *vg = lv->vg;
- struct lv_segment *seg = first_seg(lv);
-
- if (lv_is_external_origin(lv)) {
- /*
- * Since external-origin can be activated read-only,
- * there is no way to use extended areas.
- */
- log_error("Cannot resize external origin logical volume %s.",
- display_lvname(lv));
- return 0;
- }
-
- if (lv_is_raid_image(lv) || lv_is_raid_metadata(lv)) {
- log_error("Cannot resize a RAID %s directly",
- lv_is_raid_image(lv) ? "image" : "metadata area");
- return 0;
- }
-
- if (lv_is_raid_with_tracking(lv)) {
- log_error("Cannot resize logical volume %s while it is "
- "tracking a split image.", display_lvname(lv));
- return 0;
- }
-
- if ((seg_is_raid4(seg) || seg_is_any_raid5(seg)) && seg->area_count < 3) {
- log_error("Cannot resize %s LV %s. Convert to more stripes first.",
- lvseg_name(seg), display_lvname(lv));
- return 0;
- }
-
- if (lv_is_raid(lv) &&
- lp->resize == LV_REDUCE) {
- unsigned attrs = 0;
- const struct segment_type *segtype = first_seg(lv)->segtype;
-
- if (!segtype->ops->target_present ||
- !segtype->ops->target_present(lv->vg->cmd, NULL, &attrs) ||
- !(attrs & RAID_FEATURE_SHRINK)) {
- log_error("RAID module does not support shrinking.");
- return 0;
- }
- }
-
- if (!lv_is_visible(lv) &&
- !lv_is_thin_pool_metadata(lv) &&
- !lv_is_vdo_pool_data(lv) &&
- !lv_is_lockd_sanlock_lv(lv)) {
- log_error("Can't resize internal logical volume %s.", display_lvname(lv));
- return 0;
- }
-
- if (lv_is_locked(lv)) {
- log_error("Can't resize locked logical volume %s.", display_lvname(lv));
- return 0;
- }
-
- if (lv_is_converting(lv)) {
- log_error("Can't resize logical volume %s while "
- "lvconvert in progress.", display_lvname(lv));
- return 0;
- }
-
- if (lp->stripe_size) {
- if (!(vg->fid->fmt->features & FMT_SEGMENTS)) {
- log_print_unless_silent("Varied stripesize not supported. Ignoring.");
- lp->stripe_size = lp->stripes = 0;
- } else if (!_validate_stripesize(vg, lp))
- return_0;
- }
-
- if (lp->resizefs &&
- (lv_is_thin_pool(lv) ||
- lv_is_thin_pool_data(lv) ||
- lv_is_thin_pool_metadata(lv) ||
- lv_is_vdo_pool(lv) ||
- lv_is_vdo_pool_data(lv) ||
- lv_is_pool_metadata_spare(lv) ||
- lv_is_lockd_sanlock_lv(lv))) {
- log_print_unless_silent("Ignoring --resizefs as volume %s does not have a filesystem.",
- display_lvname(lv));
- lp->resizefs = 0;
- }
-
- if (lp->stripes &&
- !(vg->fid->fmt->features & FMT_SEGMENTS)) {
- log_print_unless_silent("Varied striping not supported. Ignoring.");
- lp->stripes = 0;
- }
-
- if (lp->mirrors &&
- !(vg->fid->fmt->features & FMT_SEGMENTS)) {
- log_print_unless_silent("Mirrors not supported. Ignoring.");
- lp->mirrors = 0;
- }
-
- if (lv_component_is_active(lv)) {
- log_error("Cannot resize logical volume %s with active component LV(s).",
- display_lvname(lv));
- return 0;
- }
-
- if (lv_is_integrity(lv) || lv_raid_has_integrity(lv)) {
- if (lp->resize == LV_REDUCE) {
- log_error("Cannot reduce LV with integrity.");
- return 0;
- }
- }
- return 1;
-}
-
static int _lvresize_adjust_size(struct volume_group *vg,
uint64_t size, sign_t sign,
uint32_t *extents)
@@ -5327,17 +5210,23 @@ static int _lvresize_adjust_size(struct volume_group *vg,
/*
* If percent options were used, convert them into actual numbers of extents.
+ * FIXME: fix cases where lp->extents is initially used as a percentage,
+ * and is then rewritten to be a number of extents (simply save the percent
+ * value elsewhere.)
*/
static int _lvresize_extents_from_percent(const struct logical_volume *lv,
- struct lvresize_params *lp,
- struct dm_list *pvh)
+ struct lvresize_params *lp)
{
const struct volume_group *vg = lv->vg;
uint32_t pv_extent_count;
uint32_t old_extents = lp->extents;
+ log_debug("lvresize_extents_from_percent type %d extents %u percent_value %u",
+ lp->percent, lp->extents, lp->percent_value);
+
switch (lp->percent) {
case PERCENT_VG:
+ /* rewrites lp->extents from percentage to extents */
lp->extents = percent_of_extents(lp->extents, vg->extent_count,
(lp->sign != SIGN_MINUS));
if ((lp->sign == SIGN_NONE) && (lp->extents > (lv->le_count + vg->free_count))) {
@@ -5348,21 +5237,45 @@ static int _lvresize_extents_from_percent(const struct logical_volume *lv,
}
break;
case PERCENT_FREE:
+ /* rewrites lp->extents from percentage to extents */
lp->extents = percent_of_extents(lp->extents, vg->free_count,
(lp->sign != SIGN_MINUS));
break;
case PERCENT_LV:
- lp->extents = percent_of_extents(lp->extents, lv->le_count,
- (lp->sign != SIGN_MINUS));
+ if (lp->extents) {
+ /* rewrites lp->extents from percentage to extents */
+ lp->extents = percent_of_extents(lp->extents, lv->le_count,
+ (lp->sign != SIGN_MINUS));
+ } else if (lp->percent_value) {
+ old_extents = lp->percent_value;
+ lp->extents = percent_of_extents(lp->percent_value, lv->le_count,
+ (lp->sign != SIGN_MINUS));
+ }
break;
case PERCENT_PVS:
- if (pvh != &vg->pvs) {
- pv_extent_count = pv_list_extents_free(pvh);
- lp->extents = percent_of_extents(lp->extents, pv_extent_count,
+ if (lp->pvh != &vg->pvs) {
+ pv_extent_count = pv_list_extents_free(lp->pvh);
+ if (lp->extents) {
+ /* rewrites lp->extents from percentage to extents */
+ lp->extents = percent_of_extents(lp->extents, pv_extent_count,
(lp->sign != SIGN_MINUS));
- } else
- lp->extents = percent_of_extents(lp->extents, vg->extent_count,
+ } else if (lp->percent_value) {
+ /* lvresize has PVs args and no size of exents options */
+ old_extents = lp->percent_value;
+ lp->extents = percent_of_extents(lp->percent_value, pv_extent_count,
(lp->sign != SIGN_MINUS));
+ }
+ } else {
+ if (lp->extents) {
+ /* rewrites lp->extents from percentage to extents */
+ lp->extents = percent_of_extents(lp->extents, vg->extent_count,
+ (lp->sign != SIGN_MINUS));
+ } else if (lp->percent_value) {
+ old_extents = lp->percent_value;
+ lp->extents = percent_of_extents(lp->percent_value, vg->extent_count,
+ (lp->sign != SIGN_MINUS));
+ }
+ }
break;
case PERCENT_ORIGIN:
if (!lv_is_cow(lv)) {
@@ -5430,7 +5343,7 @@ static uint32_t _lv_pe_count(struct logical_volume *lv)
/* FIXME Avoid having variables like lp->extents mean different things at different places */
static int _lvresize_adjust_extents(struct logical_volume *lv,
struct lvresize_params *lp,
- struct dm_list *pvh)
+ int *matches_existing)
{
struct volume_group *vg = lv->vg;
struct cmd_context *cmd = vg->cmd;
@@ -5711,12 +5624,12 @@ static int _lvresize_adjust_extents(struct logical_volume *lv,
}
if ((lp->extents == existing_logical_extents) && !lp->use_policies) {
- if (!lp->resizefs && !lp->poolmetadata_size) {
- log_error("New size (%d extents) matches existing size (%d extents).",
- lp->extents, existing_logical_extents);
- return 0;
- }
- lp->resize = LV_EXTEND; /* lets pretend zero size extension */
+ log_print("New size (%d extents) matches existing size (%d extents).",
+ lp->extents, existing_logical_extents);
+ if (lp->resize == LV_ANY)
+ lp->resize = LV_EXTEND; /* lets pretend zero size extension */
+ *matches_existing = 1;
+ return 1;
}
/* Perform any rounding to produce complete stripes. */
@@ -5771,12 +5684,12 @@ static int _lvresize_adjust_extents(struct logical_volume *lv,
}
lp->resize = LV_EXTEND;
} else if ((lp->extents == existing_logical_extents) && !lp->use_policies) {
- if (!lp->resizefs && !lp->poolmetadata_size) {
- log_error("New size (%d extents) matches existing size "
- "(%d extents)", lp->extents, existing_logical_extents);
- return 0;
- }
- lp->resize = LV_EXTEND;
+ log_print("New size (%d extents) matches existing size (%d extents)",
+ lp->extents, existing_logical_extents);
+ if (lp->resize == LV_ANY)
+ lp->resize = LV_EXTEND;
+ *matches_existing = 1;
+ return 1;
}
/*
@@ -5794,8 +5707,52 @@ static int _lvresize_adjust_extents(struct logical_volume *lv,
return 1;
}
-static int _lvresize_check_type(const struct logical_volume *lv,
- const struct lvresize_params *lp)
+static int _lv_reduce_vdo_discard(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct lvresize_params *lp)
+{
+ char name[PATH_MAX];
+ struct device *dev;
+ struct volume_group *vg = lv->vg;
+
+ /* FIXME: stop using dev-cache and struct device here, dev-cache
+ should only be used for scanning headers/metadata to find PVs. */
+
+ if (dm_snprintf(name, sizeof(name), "%s%s/%s", cmd->dev_dir,
+ vg->name, lv->name) < 0) {
+ log_error("Name too long - device not discarded (%s)", lv->name);
+ return 0;
+ }
+
+ if (!(dev = dev_cache_get(cmd, name, NULL))) {
+ log_error("%s: not found: device not discarded.", name);
+ return 0;
+ }
+
+ if (!dev_discard_max_bytes(cmd->dev_types, dev) ||
+ !dev_discard_granularity(cmd->dev_types, dev)) {
+ log_error("%s: max bytes and granularity query fails.", name);
+ dev_destroy_file(dev);
+ return 0;
+ }
+
+ log_warn("WARNING: %s: Discarding %s at offset " FMTu64 ", please wait...",
+ name, display_size(cmd, (uint64_t)(lv->le_count - lp->extents) * vg->extent_size),
+ ((uint64_t)lp->extents * vg->extent_size) << SECTOR_SHIFT);
+
+ if (!dev_discard_blocks(dev, ((uint64_t)lp->extents * vg->extent_size) << SECTOR_SHIFT,
+ ((uint64_t)(lv->le_count - lp->extents) * vg->extent_size) << SECTOR_SHIFT)) {
+ log_error("%s: discarding failed.", name);
+ dev_destroy_file(dev);
+ return 0;
+ }
+
+ dev_destroy_file(dev);
+ return 1;
+}
+
+static int _lv_resize_check_type(struct logical_volume *lv,
+ struct lvresize_params *lp)
{
struct lv_segment *seg;
@@ -5812,6 +5769,20 @@ static int _lvresize_check_type(const struct logical_volume *lv,
}
}
+ if (lv_is_raid_image(lv) || lv_is_raid_metadata(lv)) {
+ log_error("Cannot resize a RAID %s directly for %s",
+ lv_is_raid_image(lv) ? "image" : "metadata area",
+ display_lvname(lv));
+ return 0;
+ }
+
+ seg = first_seg(lv);
+ if ((seg_is_raid4(seg) || seg_is_any_raid5(seg)) && seg->area_count < 3) {
+ log_error("Cannot resize %s LV %s. Convert to more stripes first.",
+ lvseg_name(seg), display_lvname(lv));
+ return 0;
+ }
+
if (lp->resize == LV_REDUCE) {
if (lv_is_thin_pool_data(lv)) {
log_error("Thin pool volumes %s cannot be reduced in size yet.",
@@ -5832,6 +5803,21 @@ static int _lvresize_check_type(const struct logical_volume *lv,
log_error("Reduce not yet allowed on LVs with writecache attached.");
return 0;
}
+ if (lv_is_raid(lv)) {
+ unsigned attrs = 0;
+ const struct segment_type *segtype = first_seg(lv)->segtype;
+
+ if (!segtype->ops->target_present ||
+ !segtype->ops->target_present(lv->vg->cmd, NULL, &attrs) ||
+ !(attrs & RAID_FEATURE_SHRINK)) {
+ log_error("RAID module does not support shrinking.");
+ return 0;
+ }
+ }
+ if (lv_is_integrity(lv) || lv_raid_has_integrity(lv)) {
+ log_error("Cannot reduce LV with integrity.");
+ return 0;
+ }
} else if (lp->resize == LV_EXTEND) {
if (lv_is_thin_pool_metadata(lv) &&
(!(seg = find_pool_seg(first_seg(lv))) ||
@@ -5856,12 +5842,15 @@ static int _lvresize_check_type(const struct logical_volume *lv,
return 0;
}
+ if ((lp->resize == LV_REDUCE) && (lp->pvh != &lv->vg->pvs))
+ log_print_unless_silent("Ignoring PVs on command line when reducing.");
+
return 1;
}
-static int _lvresize_volume(struct logical_volume *lv,
- struct lvresize_params *lp,
- struct dm_list *pvh)
+static int _lv_resize_volume(struct logical_volume *lv,
+ struct lvresize_params *lp,
+ struct dm_list *pvh)
{
struct volume_group *vg = lv->vg;
struct cmd_context *cmd = vg->cmd;
@@ -5895,24 +5884,32 @@ static int _lvresize_volume(struct logical_volume *lv,
display_lvname(lv),
display_size(cmd, (uint64_t) old_extents * vg->extent_size), old_extents,
display_size(cmd, (uint64_t) lv->le_count * vg->extent_size), lv->le_count);
-
- /* Resizing metadata and PV list is not specified -> maintain size of _pmspare volume */
- if ((&vg->pvs == pvh) && lv_is_pool_metadata(lv))
- (void) handle_pool_metadata_spare(vg, 0, pvh, 1);
}
return 1;
}
-static int _lvresize_prepare(struct logical_volume *lv,
- struct lvresize_params *lp,
- struct dm_list *pvh)
+static int _lv_resize_adjust_size(struct logical_volume *lv,
+ struct lvresize_params *lp,
+ int *matches_existing)
{
/* Resolve extents from size */
- if (lp->size && !_lvresize_adjust_size(lv->vg, lp->size, lp->sign, &lp->extents))
- return_0;
- else if (lp->extents && !_lvresize_extents_from_percent(lv, lp, pvh))
- return_0;
+ if (lp->size) {
+ if (!_lvresize_adjust_size(lv->vg, lp->size, lp->sign, &lp->extents))
+ return_0;
+ }
+
+ /* set lp->extents based on lp->percent_value */
+ else if (lp->percent_value) {
+ if (!_lvresize_extents_from_percent(lv, lp))
+ return_0;
+ }
+
+ /* rewrites lp->extents from percentage to extents */
+ else if (lp->extents && (lp->percent != PERCENT_NONE)) {
+ if (!_lvresize_extents_from_percent(lv, lp))
+ return_0;
+ }
/* Ensure stripe boundary extents! */
if (!lp->percent && lv_is_raid(lv))
@@ -5920,18 +5917,15 @@ static int _lvresize_prepare(struct logical_volume *lv,
seg_is_raid1(first_seg(lv)) ? 0 : _raid_stripes_count(first_seg(lv)),
lp->resize == LV_REDUCE ? 0 : 1);
- if (!_lvresize_adjust_extents(lv, lp, pvh))
- return_0;
-
- if (!_lvresize_check_type(lv, lp))
+ if (!_lvresize_adjust_extents(lv, lp, matches_existing))
return_0;
return 1;
}
-/* Set aux LV properties, we can't use those from command line */
-static struct logical_volume *_lvresize_setup_aux(struct logical_volume *lv,
- struct lvresize_params *lp)
+/* Set thin pool metadata properties, we can't use those from command line */
+static void _setup_params_for_extend_metadata(struct logical_volume *lv,
+ struct lvresize_params *lp)
{
struct lv_segment *mseg = last_seg(lv);
@@ -5942,272 +5936,466 @@ static struct logical_volume *_lvresize_setup_aux(struct logical_volume *lv,
lp->resizefs = 0;
lp->stripes = lp->mirrors ? mseg->area_count / lp->mirrors : 0;
lp->stripe_size = mseg->stripe_size;
+}
- return lv;
+
+static int _lv_resize_check_used(struct logical_volume *lv)
+{
+ if (lv_is_locked(lv)) {
+ log_error("Can't resize locked logical volume %s.", display_lvname(lv));
+ return 0;
+ }
+
+ if (lv_is_converting(lv)) {
+ log_error("Can't resize logical volume %s while lvconvert in progress.", display_lvname(lv));
+ return 0;
+ }
+
+ if (lv_component_is_active(lv)) {
+ log_error("Cannot resize logical volume %s with active component LV(s).", display_lvname(lv));
+ return 0;
+ }
+
+ if (lv_is_raid_with_tracking(lv)) {
+ log_error("Cannot resize logical volume %s while it is tracking a split image.", display_lvname(lv));
+ return 0;
+ }
+
+ if (lv_is_external_origin(lv)) {
+ /*
+ * Since external-origin can be activated read-only,
+ * there is no way to use extended areas.
+ */
+ log_error("Cannot resize external origin logical volume %s.",
+ display_lvname(lv));
+ return 0;
+ }
+
+ return 1;
}
-int lv_resize(struct logical_volume *lv,
- struct lvresize_params *lp,
- struct dm_list *pvh)
+int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
+ struct lvresize_params *lp)
{
+ struct lvresize_params lp_meta;
struct volume_group *vg = lv->vg;
- struct cmd_context *cmd = vg->cmd;
- struct logical_volume *lock_lv = (struct logical_volume*) lv_lock_holder(lv);
- struct logical_volume *aux_lv = NULL; /* Note: aux_lv never resizes fs */
- struct logical_volume *resizable_layer_lv;
- struct lvresize_params aux_lp;
struct lv_segment *seg = first_seg(lv);
+ struct logical_volume *lv_top = NULL;
+ struct logical_volume *lv_main = NULL;
+ struct logical_volume *lv_meta = NULL;
+ struct logical_volume *lv_main_layer = NULL;
+ struct logical_volume *lv_meta_layer = NULL;
+ int main_size_matches = 0;
+ int meta_size_matches = 0;
+ int is_extend = (lp->resize == LV_EXTEND);
+ int is_reduce = (lp->resize == LV_REDUCE);
int activated = 0;
- int ret = 0;
int status;
- struct device *dev;
- char name[PATH_MAX];
+ int ret = 0;
+
+ memset(&lp_meta, 0, sizeof(lp_meta));
+
+ /*
+ * Some checks apply to the LV command arg (don't require top/bottom
+ * LVs in a stack), and don't require knowing if the command is doing
+ * extend or reduce (determined later).
+ */
+
+ if (lp->stripe_size && !_validate_stripesize(vg, lp))
+ return_0;
+
+ /*
+ * The only types of !visible/internal/non-top LVs that can be directly
+ * resized via the command arg. Other internal LVs are resized
+ * indirectly when resizing a top LV.
+ */
+ if (!lv_is_visible(lv) &&
+ !lv_is_thin_pool_data(lv) &&
+ !lv_is_thin_pool_metadata(lv) &&
+ !lv_is_vdo_pool_data(lv) &&
+ !lv_is_lockd_sanlock_lv(lv)) {
+ log_error("Can't resize internal logical volume %s.", display_lvname(lv));
+ return 0;
+ }
- if (lp->use_policies) {
- if (!lv_is_cow(lv) &&
- !lv_is_thin_pool(lv) &&
- !lv_is_vdo_pool(lv)) {
- log_error("Policy-based resize is supported only for snapshot, thin pool and vdo pool volumes.");
+ /*
+ * resizefs applies to the LV command arg, not to the top LV or lower
+ * resizable LV.
+ */
+ if (lp->resizefs) {
+ if (!lv_is_active(lv)) {
+ log_error("Logical volume %s must be activated before resizing filesystem.",
+ display_lvname(lv));
return 0;
}
+ /* types of LVs that can hold a file system */
+ if (!(lv_is_linear(lv) || lv_is_striped(lv) || lv_is_raid(lv) ||
+ lv_is_mirror(lv) || lv_is_thin_volume(lv) || lv_is_vdo(lv) ||
+ lv_is_cache(lv) || lv_is_writecache(lv))) {
+ log_print_unless_silent("Ignoring --resizefs for LV type %s.",
+ seg ? seg->segtype->name : "unknown");
+ lp->resizefs = 0;
+ }
+ }
- lp->extents = 0;
- lp->sign = SIGN_PLUS;
- lp->percent = PERCENT_LV;
+ /*
+ * Figure out which LVs are going to be extended, and set params
+ * to the requested extents/size for each. Some LVs are extended
+ * only by extending an underlying LV. Extending some top level
+ * LVs results in extending multiple underlying LVs.
+ *
+ * lv_top is the top level LV in stack.
+ * lv_main is the main LV to be resized.
+ * lv_meta is always a thin pool metadata LV.
+ *
+ * lv_main_layer/lv_meta_layer may be LV types (like cache) that are
+ * layered over the main/meta LVs. These layer LVs are skipped over
+ * by get_resizable_layer_lv() which finds the bottom-most layer
+ * which is originally resized. The layer LVs are resized indirectly
+ * as a result of the lower data-holding LVs being resized.
+ *
+ * In the simplest case there is no layering/stacking, and
+ * lv == lv_main == lv_main_layer == lv_top
+ */
- aux_lp = *lp;
- if (!_lvresize_adjust_policy(lv, &lp->extents, &aux_lp.extents))
+ if (cmd->command_enum == lvextend_policy_CMD) {
+ /* lvextend --use-policies may extend main or meta or both */
+ lv_top = lv;
+ if (lv_is_thin_pool(lv)) {
+ if (lp->policy_percent_main) {
+ lv_main = seg_lv(first_seg(lv), 0); /* thin pool data */
+ lp->percent_value = lp->policy_percent_main;
+ }
+ if (lp->policy_percent_meta) {
+ lv_meta = first_seg(lv)->metadata_lv; /* thin pool metadata */
+ _setup_params_for_extend_metadata(lv_meta, &lp_meta);
+ /* override setup function which isn't right for policy use */
+ lp_meta.percent = PERCENT_LV;
+ lp_meta.sign = SIGN_PLUS;
+ lp_meta.percent_value = lp->policy_percent_meta;
+ lp_meta.pvh = lp->pvh;
+ }
+ } else if (lv_is_vdo_pool(lv)) {
+ lv_main = seg_lv(first_seg(lv), 0); /* vdo pool data */
+ lp->percent_value = lp->policy_percent_main;
+ } else if (lv_is_cow(lv)) {
+ lv_main = lv;
+ lp->percent_value = lp->policy_percent_main;
+ } else
return_0;
- if (!lp->extents) {
- if (!aux_lp.extents)
- return 1; /* Nothing to do */
- /* Resize thin-pool metadata as mainlv */
- lv = seg->metadata_lv; /* metadata LV */
- lp->extents = aux_lp.extents;
- } else if (aux_lp.extents) {
- /* Also resize thin-pool metadata */
- aux_lv = _lvresize_setup_aux(seg->metadata_lv, &aux_lp);
- }
- } else if (lp->poolmetadata_size) {
- /* Validate --poolmetadata_size supported volumes here */
- if (!lv_is_thin_pool(lv) && !lv_is_thin_pool_metadata(lv)) {
+ } else if ((cmd->command_enum == lvextend_pool_metadata_CMD) ||
+ (cmd->command_enum == lvresize_pool_metadata_CMD)) {
+ /* lvresize|lvextend --poolmetadatasize, extends only thin pool metadata */
+ if (lv_is_thin_pool(lv)) {
+ lv_top = lv;
+ lv_meta = first_seg(lv)->metadata_lv; /* thin pool metadata */
+ } else if (lv_is_thin_pool_metadata(lv)) {
+ lv_top = _get_top_layer_lv(lv); /* thin pool LV */
+ lv_meta = lv;
+ } else {
log_error("--poolmetadatasize can be used only with thin pools.");
return 0;
}
- if (!lp->extents && !lp->size) {
- /* When only --poolmetadatasize given and any size option
- * switch directly to resize metadata LV */
- if (lv_is_thin_pool(lv))
- lv = seg->metadata_lv;
- lp->size = lp->poolmetadata_size;
- lp->sign = lp->poolmetadata_sign;
+ lp_meta = *lp;
+ _setup_params_for_extend_metadata(lv_meta, &lp_meta);
+ lp_meta.size = lp->poolmetadata_size;
+ lp_meta.sign = lp->poolmetadata_sign;
+ lp->poolmetadata_size = 0;
+ lp->poolmetadata_sign = 0;
+
+ } else if (lv_is_thin_pool(lv) && lp->poolmetadata_size) {
+ /* extend both thin pool data and metadata */
+ lv_top = lv;
+ lv_main = seg_lv(first_seg(lv), 0); /* thin pool data */
+ lv_meta = first_seg(lv)->metadata_lv; /* thin pool metadata */
+ lp_meta = *lp;
+ _setup_params_for_extend_metadata(lv_meta, &lp_meta);
+ lp_meta.size = lp->poolmetadata_size;
+ lp_meta.sign = lp->poolmetadata_sign;
+ lp->poolmetadata_size = 0;
+ lp->poolmetadata_sign = 0;
+
+ } else if (lv_is_thin_pool_metadata(lv)) {
+ /* extend only thin pool metadata */
+ lv_top = _get_top_layer_lv(lv); /* thin pool LV */
+ lv_meta = lv;
+ lp_meta = *lp;
+ _setup_params_for_extend_metadata(lv_meta, &lp_meta);
+ if (lp->poolmetadata_size) {
+ lp_meta.size = lp->poolmetadata_size;
+ lp_meta.size = lp->poolmetadata_sign;
lp->poolmetadata_size = 0;
- } else if (lv_is_thin_pool(lv)) {
- aux_lp = *lp;
- aux_lv = _lvresize_setup_aux(seg->metadata_lv, &aux_lp);
- aux_lp.size = lp->poolmetadata_size;
- aux_lp.sign = lp->poolmetadata_sign;
- } else {
- log_error("--poolmetadatasize for thin-pool metadata cannot be used with size.");
- return 0;
+ lp->poolmetadata_sign = 0;
}
+ /* else lp_meta.extents|size from lp->extents|size above */
+
+ } else if (lv_is_thin_pool(lv)) {
+ /* extend thin pool data and possibly metadata */
+ lv_top = lv;
+ lv_main = seg_lv(first_seg(lv), 0);
+ /* Do not set lv_meta to the thin pool metadata here.
+ See below "Possibly enable lv_meta extend". */
}
- resizable_layer_lv = _get_resizable_layer_lv(lv);
- if (!(lv = _get_top_layer_lv(lv)))
- return_0;
+ /*
+ * None of the special cases above (selecting which LVs to extend
+ * depending on options set and type of LV) have applied, so this
+ * is the standard case.
+ */
+ if (!lv_main && !lv_meta) {
+ lv_top = _get_top_layer_lv(lv);
+ lv_main_layer = lv;
+ lv_main = _get_resizable_layer_lv(lv_main_layer);
+ } else {
+ lv_main_layer = lv_main;
+ lv_meta_layer = lv_meta;
+ if (lv_main)
+ lv_main = _get_resizable_layer_lv(lv_main_layer);
+ if (lv_meta)
+ lv_meta = _get_resizable_layer_lv(lv_meta_layer);
+ }
+ /* Clear layer variables if no layer exists. */
+ if (lv_main_layer == lv_main)
+ lv_main_layer = NULL;
+ if (lv_meta_layer == lv_meta)
+ lv_meta_layer = NULL;
- if (!_lvresize_check(lv, lp))
- return_0;
+ /*
+ * LVs to work with are now determined:
+ * lv_top is always set, it is not used to resize, but is used
+ * to reload dm devices for the lv.
+ * If lv_main is set, it is resized.
+ * If lv_meta is set, it is resized.
+ * If lv_meta is not set, it may be set below and resized.
+ */
- /* Always should have lp->size or lp->extents */
- if (!_lvresize_prepare(resizable_layer_lv, lp, pvh))
+ if (!_lv_resize_check_used(lv_top))
return_0;
-
- if ((lp->resize != LV_REDUCE) && !aux_lv && !lp->poolmetadata_size &&
- (&vg->pvs == pvh) && lv_is_thin_pool(lv)) {
- /* When thin-pool data part is extended, automatically extend also metadata part
- * to have the metadata chunks for adressing all data blocks
- * Do this only when PV list is not defined and --poolmetadatasize is unspecified */
- aux_lp = *lp;
- seg = first_seg(lv);
- aux_lp.size = estimate_thin_pool_metadata_size(lp->extents, vg->extent_size, seg->chunk_size);
- if (aux_lp.size > seg->metadata_lv->size) {
- log_verbose("Also going to resize thin-pool metadata to match bigger data.");
- aux_lv = _lvresize_setup_aux(seg->metadata_lv, &aux_lp);
- aux_lp.sign = SIGN_NONE;
- } else
- aux_lp.size = 0;
- }
-
- if (aux_lv && (aux_lv = _get_resizable_layer_lv(aux_lv)) &&
- !_lvresize_prepare(aux_lv, &aux_lp, pvh))
+ if (lv_main && (lv_main != lv_top) && !_lv_resize_check_used(lv_main))
return_0;
- if (((lp->resize == LV_REDUCE) ||
- (aux_lv && aux_lp.resize == LV_REDUCE)) &&
- (pvh != &vg->pvs))
- log_print_unless_silent("Ignoring PVs on command line when reducing.");
+ /*
+ * Set a new size for lv_main.
+ */
+ if (lv_main) {
+ /* sets lp extents and lp resize */
+ if (!_lv_resize_adjust_size(lv_main, lp, &main_size_matches))
+ return_0;
+ /* sanity check the result of adjust_size */
+ if (lp->extents == 0)
+ return_0;
+ /* adjust_size resolves LV_ANY to EXTEND|REDUCE */
+ if (lp->resize == LV_ANY)
+ return_0;
+ if (is_extend && (lp->resize != LV_EXTEND))
+ return_0;
+ if (is_reduce && (lp->resize != LV_REDUCE))
+ return_0;
+ is_extend = (lp->resize == LV_EXTEND);
+ is_reduce = (lp->resize == LV_REDUCE);
- /* Request confirmation before operations that are often mistakes. */
- /* aux_lv never resize fs */
- if ((lp->resizefs || (lp->resize == LV_REDUCE)) &&
- !_request_confirmation(lv, lp))
- return_0;
+ if (!_lv_resize_check_type(lv_main, lp))
+ return_0;
- if (lp->resizefs) {
- if (!lp->nofsck &&
- !_fsadm_cmd(FSADM_CMD_CHECK, lv, 0, lp->yes, lp->force, &status)) {
- if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) {
- log_error("Filesystem check failed.");
- return 0;
- }
- /* some filesystems support online resize */
- }
+ if (is_reduce && !main_size_matches && !lp->resizefs &&
+ !_lv_reduce_confirmation(lv, lp))
+ return_0;
+ }
- /* FIXME forks here */
- if ((lp->resize == LV_REDUCE) &&
- !_fsadm_cmd(FSADM_CMD_RESIZE, lv, lp->extents, lp->yes, lp->force, NULL)) {
- log_error("Filesystem resize failed.");
- return 0;
+ /*
+ * Possibly enable lv_meta extend if not already enabled. If lv_meta
+ * for a thin pool is not already being extended, and user requested
+ * extending the thin pool, then we may need to automatically include
+ * extending lv_meta in addition to lv_main (data), so that the
+ * metadata size is sufficient for the extended data size.
+ *
+ * If specific PVs were named to extend, this is taken to mean that
+ * only the thin pool data should be extended (using those PVs), and
+ * the thin pool metadata should not be automatically extended (since
+ * it would likely want to be extended using different PVs.)
+ */
+ if (lv_is_thin_pool(lv_top) && is_extend && lv_main && !lv_meta && (&vg->pvs == lp->pvh)) {
+ struct lv_segment *tpseg = first_seg(lv_top);
+ uint64_t meta_size = estimate_thin_pool_metadata_size(lp->extents, vg->extent_size, tpseg->chunk_size);
+ if (meta_size > tpseg->metadata_lv->size) {
+ log_verbose("Extending thin pool metadata to %llu for larger data", (unsigned long long)meta_size);
+ lv_meta = tpseg->metadata_lv;
+ lp_meta = *lp;
+ _setup_params_for_extend_metadata(lv_meta, &lp_meta);
+ lp_meta.size = meta_size;
+ lp_meta.sign = SIGN_NONE;
+ /* meta may have a layer over it */
+ lv_meta_layer = lv_meta;
+ lv_meta = _get_resizable_layer_lv(lv_meta_layer);
+ if (lv_meta == lv_meta_layer)
+ lv_meta_layer = NULL;
}
}
- if (!lp->extents && (!aux_lv || !aux_lp.extents)) {
- lp->extents = lv->le_count;
- goto out; /* Nothing to do */
+ /*
+ * Set a new size for lv_meta (extend only.)
+ */
+ if (lv_meta) {
+ /* sets lp extents and lp resize */
+ if (!_lv_resize_adjust_size(lv_meta, &lp_meta, &meta_size_matches))
+ return_0;
+ /* sanity check the result of adjust_size */
+ if (lp_meta.extents == 0)
+ return_0;
+ /* adjust_size resolves lp_meta.resize to EXTEND|REDUCE */
+ /* _lv_resize_check_type errors if resize is EXTEND for thin meta */
+ if (!_lv_resize_check_type(lv_meta, &lp_meta))
+ return_0;
}
- if (lv_is_thin_pool(lock_lv) && /* Lock holder is thin-pool */
- !lv_is_active(lock_lv)) {
+ /*
+ * No resizing is needed.
+ */
+ if ((main_size_matches && meta_size_matches) ||
+ (main_size_matches && !lv_meta) ||
+ (meta_size_matches && !lv_main)) {
+ log_error("No size change.");
+ return 0;
+ }
+ /*
+ * Active 'hidden' -tpool can be waiting for resize, but the pool LV
+ * itself might be inactive. Here plain suspend/resume would not work.
+ * So active temporarily pool LV (with on disk metadata) then use
+ * suspend and resume and deactivate pool LV, instead of searching for
+ * an active thin volume.
+ */
+ if (lv_is_thin_pool(lv_top) && !lv_is_active(lv_top)) {
if (!activation()) {
- log_error("Cannot resize %s without using "
- "device-mapper kernel driver.",
- display_lvname(lock_lv));
- return 0;
+ log_error("Cannot resize %s without using device-mapper kernel driver.",
+ display_lvname(lv_top));
+ return_0;
}
- /*
- * Active 'hidden' -tpool can be waiting for resize, but the
- * pool LV itself might be inactive.
- * Here plain suspend/resume would not work.
- * So active temporarily pool LV (with on disk metadata)
- * then use suspend and resume and deactivate pool LV,
- * instead of searching for an active thin volume.
- */
- if (!activate_lv(cmd, lock_lv)) {
- log_error("Failed to activate %s.", display_lvname(lock_lv));
- return 0;
+ if (!activate_lv(cmd, lv_top)) {
+ log_error("Failed to activate %s.", display_lvname(lv_top));
+ return_0;
}
-
activated = 1;
}
- /* Send DISCARD/TRIM to reduced area of VDO volumes
- * TODO: enable thin and provide
- * TODO2: we need polling method */
- if ((lp->resize == LV_REDUCE) && lv_is_vdo(lv)) {
- if (dm_snprintf(name, sizeof(name), "%s%s/%s", lv->vg->cmd->dev_dir,
- lv->vg->name, lv->name) < 0) {
- log_error("Name too long - device not discarded (%s)", lv->name);
- return 0;
- }
-
- if (!(dev = dev_cache_get(lv->vg->cmd, name, NULL))) {
- log_error("%s: not found: device not discarded.", name);
- return 0;
- }
+ /*
+ * If the LV is locked due to being active, this lock call is a no-op.
+ * Otherwise, this acquires a transient lock on the lv (not PERSISTENT)
+ */
+ if (!lockd_lv_resize(cmd, lv_top, "ex", 0, lp))
+ goto_out;
- if (!dev_discard_max_bytes(cmd->dev_types, dev) ||
- !dev_discard_granularity(cmd->dev_types, dev)) {
- log_error("%s: max bytes and granularity query fails.", name);
- dev_destroy_file(dev);
- return 0;
+ /*
+ * Check the file system, and shrink the fs before reducing lv.
+ * TODO: libblkid fs type, fs last_block, mount state,
+ * unlock vg, mount|unmount if needed, fork fs shrink,
+ * lock vg, rescan devs, recheck fs type last_block.
+ * (at end mount|unmount if needed to restore initial state.)
+ */
+ if (lp->resizefs && !lp->nofsck &&
+ !_fsadm_cmd(FSADM_CMD_CHECK, lv_top, 0, lp->yes, lp->force, &status)) {
+ if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) {
+ log_error("Filesystem check failed.");
+ goto out;
}
+ /* some filesystems support online resize */
+ }
+ if (lp->resizefs && is_reduce &&
+ !_fsadm_cmd(FSADM_CMD_RESIZE, lv_top, lp->extents, lp->yes, lp->force, NULL)) {
+ log_error("Filesystem resize failed.");
+ goto out;
+ }
- log_warn("WARNING: %s: Discarding %s at offset " FMTu64 ", please wait...",
- name, display_size(cmd, (uint64_t)(lv->le_count - lp->extents) * vg->extent_size),
- ((uint64_t)lp->extents * vg->extent_size) << SECTOR_SHIFT);
-
- if (!dev_discard_blocks(dev, ((uint64_t)lp->extents * vg->extent_size) << SECTOR_SHIFT,
- ((uint64_t)(lv->le_count - lp->extents) * vg->extent_size) << SECTOR_SHIFT)) {
- log_error("%s: discarding failed.", name);
- dev_destroy_file(dev);
- return 0;
- }
+ /*
+ * Send DISCARD/TRIM to reduced area of VDO volumes
+ * TODO: enable thin and provide
+ * TODO2: we need polling method
+ */
+ if (is_reduce && lv_is_vdo(lv_top) && !_lv_reduce_vdo_discard(cmd, lv_top, lp))
+ goto_out;
- dev_destroy_file(dev);
- }
/*
- * If the LV is locked from activation, this lock call is a no-op.
- * Otherwise, this acquires a transient lock on the lv (not PERSISTENT).
+ * Remove any striped raid reshape space for LV resizing (not common).
*/
- if (!lockd_lv_resize(cmd, lock_lv, "ex", 0, lp))
+ if (lv_meta && first_seg(lv_meta)->reshape_len && !lv_raid_free_reshape_space(lv_meta))
+ return_0;
+ if (lv_main && first_seg(lv_main)->reshape_len && !lv_raid_free_reshape_space(lv_main))
return_0;
- /* Remove any striped raid reshape space for LV resizing */
- if (aux_lv && first_seg(aux_lv)->reshape_len)
- if (!lv_raid_free_reshape_space(aux_lv))
- return_0;
- if (first_seg(resizable_layer_lv)->reshape_len)
- if (!lv_raid_free_reshape_space(resizable_layer_lv))
- return_0;
-
- if (aux_lv) {
- if (!_lvresize_volume(aux_lv, &aux_lp, pvh))
- goto_bad;
+ /*
+ * The core of the actual lv resizing.
+ * Allocate or free extents in the VG, adjust LV segments to reflect
+ * new requested size, write VG metadata, reload the dm device stack
+ * (reload from the top LV.) Do lv_meta first.
+ * When extending lv_meta, also extend (or create) the pool's spare
+ * meta lv to match the size of lv_meta (only do this when the
+ * command is not limited to allocating from specific PVs.)
+ */
- /* store vg on disk(s) */
- if (aux_lp.size_changed && !lv_update_and_reload(lock_lv))
- goto_bad;
- }
+ if (!lv_meta)
+ goto do_main;
+ if (!_lv_resize_volume(lv_meta, &lp_meta, lp->pvh))
+ goto_out;
+ if (!lp_meta.size_changed)
+ goto do_main;
+ if ((&vg->pvs == lp->pvh) && !handle_pool_metadata_spare(vg, 0, lp->pvh, 1))
+ stack;
+ if (!lv_update_and_reload(lv_top))
+ goto_out;
+ log_debug("Resized thin pool metadata %s to %u extents.", display_lvname(lv_meta), lp_meta.extents);
- if (!_lvresize_volume(resizable_layer_lv, lp, pvh))
- goto_bad;
+ do_main:
- /* store vg on disk(s) */
+ if (!lv_main)
+ goto end_main;
+ if (!_lv_resize_volume(lv_main, lp, lp->pvh))
+ goto_out;
if (!lp->size_changed)
- goto out; /* No table reload needed */
+ goto_out;
+ if (!lv_update_and_reload(lv_top))
+ goto_out;
+ log_debug("Resized %s to %u extents.", display_lvname(lv_main), lp->extents);
- if (!lv_update_and_reload(lock_lv))
- goto_bad;
+ end_main:
- if (lv_is_cow_covering_origin(lv))
- if (!monitor_dev_for_events(cmd, lv, 0, 0))
+ /*
+ * other maintenance:
+ * - update lvm pool metadata (drop messages).
+ * - print warnings about overprovisioning.
+ * - stop monitoring cow snapshot larger than origin
+ */
+ if (lv_is_thin_pool(lv_top)) {
+ if (!update_thin_pool_lv(lv_top, 1))
+ goto_out;
+ if (is_extend)
+ thin_pool_check_overprovisioning(lv_top);
+ }
+ if (lv_main && lv_is_cow_covering_origin(lv_main)) {
+ if (!monitor_dev_for_events(cmd, lv_main, 0, 0))
stack;
-
- if (lv_is_thin_pool(lock_lv)) {
- /* Update lvm pool metadata (drop messages). */
- if (!update_thin_pool_lv(lock_lv, 1))
- goto_bad;
}
- /* Check for over provisioning when extended */
- if ((lp->resize == LV_EXTEND) && lv_is_thin_type(lv))
- thin_pool_check_overprovisioning(lv);
-
-out:
- log_print_unless_silent("Logical volume %s successfully resized.",
- display_lvname(lv));
-
- if (lp->resizefs && (lp->resize == LV_EXTEND) &&
- !_fsadm_cmd(FSADM_CMD_RESIZE, lv, lp->extents, lp->yes, lp->force, NULL))
- goto_bad;
+ /*
+ * Extend the file system.
+ * TODO: libblkid fs type, mount state,
+ * unlock vg, mount|unmount if needed, fork fs grow,
+ * mount|unmount if needed to restore initial state.
+ */
+ if (lp->resizefs && is_extend &&
+ !_fsadm_cmd(FSADM_CMD_RESIZE, lv_top, lp->extents, lp->yes, lp->force, NULL)) {
+ log_warn("Filesystem resize failed.");
+ goto out;
+ }
ret = 1;
-bad:
- if (activated) {
- if (!sync_local_dev_names(lock_lv->vg->cmd)) {
- log_error("Failed to sync local devices before deactivating LV %s.",
- display_lvname(lock_lv));
- return 0;
- }
- if (!deactivate_lv(cmd, lock_lv)) {
- log_error("Problem deactivating %s.", display_lvname(lock_lv));
- ret = 0;
- }
+ out:
+ if (activated) {
+ if (!sync_local_dev_names(cmd))
+ stack;
+ if (!deactivate_lv(cmd, lv_top))
+ log_warn("Problem deactivating %s.", display_lvname(lv_top));
}
return ret;
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index deaf3f30c..048b7553f 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -297,6 +297,9 @@
int lv_layout_and_role(struct dm_pool *mem, const struct logical_volume *lv,
struct dm_list **layout, struct dm_list **role);
+int lv_is_linear(struct logical_volume *lv);
+int lv_is_striped(struct logical_volume *lv);
+
/* Ordered list - see lv_manip.c */
typedef enum {
AREA_UNASSIGNED,
@@ -661,42 +664,33 @@ struct pvcreate_params {
};
struct lvresize_params {
- int argc;
- char **argv;
-
- const char *vg_name; /* only-used when VG is not yet opened (in /tools) */
- const char *lv_name;
-
- const struct segment_type *segtype;
-
- uint64_t poolmetadata_size;
- sign_t poolmetadata_sign;
-
- /* Per LV applied parameters */
-
enum {
LV_ANY = 0,
LV_REDUCE = 1,
LV_EXTEND = 2
} resize;
-
- int use_policies;
-
alloc_policy_t alloc;
int yes;
int force;
int nosync;
int nofsck;
int resizefs;
+ int use_policies;
+ const struct segment_type *segtype;
unsigned mirrors;
uint32_t stripes;
uint64_t stripe_size;
- uint32_t extents;
uint64_t size;
+ uint32_t extents;
sign_t sign;
- percent_type_t percent;
+ percent_type_t percent; /* the type of percentage, not a value */
+ uint32_t percent_value; /* 0 - 100 */
+ uint64_t poolmetadata_size;
+ sign_t poolmetadata_sign;
+ uint32_t policy_percent_main;
+ uint32_t policy_percent_meta;
int approx_alloc;
int extents_are_pes; /* Is 'extents' counting PEs or LEs? */
@@ -705,6 +699,8 @@ struct lvresize_params {
const char *lockopt;
char *lockd_lv_refresh_path; /* set during resize to use for refresh at the end */
char *lockd_lv_refresh_uuid; /* set during resize to use for refresh at the end */
+
+ struct dm_list *pvh; /* list of pvs to use */
};
void pvcreate_params_set_defaults(struct pvcreate_params *pp);
@@ -745,9 +741,10 @@ int vgs_are_compatible(struct cmd_context *cmd,
struct volume_group *vg_to);
uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname);
-int lv_resize(struct logical_volume *lv,
- struct lvresize_params *lp,
- struct dm_list *pvh);
+int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
+ struct lvresize_params *lp);
+int lv_extend_policy_calculate_percent(struct logical_volume *lv,
+ uint32_t *amount, uint32_t *meta_amount);
struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const char *vgid,
uint32_t read_flags, uint32_t lockd_state,
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 1cda1888f..95f25eef8 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -5302,3 +5302,15 @@ int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, s
return 1;
}
+int lv_is_linear(struct logical_volume *lv)
+{
+ struct lv_segment *seg = first_seg(lv);
+ return segtype_is_linear(seg->segtype);
+}
+
+int lv_is_striped(struct logical_volume *lv)
+{
+ struct lv_segment *seg = first_seg(lv);
+ return segtype_is_striped(seg->segtype);
+}
+
diff --git a/lib/metadata/pool_manip.c b/lib/metadata/pool_manip.c
index bb4b5f115..a6bfc2deb 100644
--- a/lib/metadata/pool_manip.c
+++ b/lib/metadata/pool_manip.c
@@ -737,6 +737,7 @@ int handle_pool_metadata_spare(struct volume_group *vg, uint32_t extents,
extents = MAX_SIZE;
if (!lv) {
+ log_debug("Adding new pool metadata spare %u extents.", extents);
if (!_alloc_pool_metadata_spare(vg, extents, pvh))
return_0;
@@ -746,6 +747,8 @@ int handle_pool_metadata_spare(struct volume_group *vg, uint32_t extents,
seg = last_seg(lv);
seg_mirrors = lv_mirror_count(lv);
+ log_debug("Extending pool metadata spare from %u to %u extents.",
+ lv->le_count, extents);
/* Check spare LV is big enough and preserve segtype */
if ((lv->le_count < extents) && seg &&
!lv_extend(lv, seg->segtype,
diff --git a/test/shell/lvextend-thin-full.sh b/test/shell/lvextend-thin-full.sh
index 263ab2fb9..69d15e3b0 100644
--- a/test/shell/lvextend-thin-full.sh
+++ b/test/shell/lvextend-thin-full.sh
@@ -41,16 +41,15 @@ lvcreate -L2M -n $lv1 $vg
"$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1"
lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1
-# Cannot resize if set to 0%
-not lvextend --use-policies --config 'activation{thin_pool_autoextend_percent = 0}' $vg/pool 2>&1 | tee err
-grep "0%" err
-
-# Locally active LV is needed
+# active thin pool is needed to use policy
not lvextend --use-policies $vg/pool 2>&1 | tee err
-grep "locally" err
lvchange -ay $vg
+# Cannot resize if set to 0%
+not lvextend --use-policies --config 'activation{thin_pool_autoextend_percent = 0}' $vg/pool 2>&1 | tee err
+grep "0%" err
+
# Creation of new LV is not allowed when thinpool is over threshold
not lvcreate -V10 $vg/pool
diff --git a/test/shell/lvresize-full.sh b/test/shell/lvresize-full.sh
index dcf93a7b2..1ad9f32b1 100644
--- a/test/shell/lvresize-full.sh
+++ b/test/shell/lvresize-full.sh
@@ -37,15 +37,19 @@ lvs -a $vg
"$MKFS" "$lvdev"
# this should resolve to resize to same actual size
-lvreduce -r -f -l-100%FREE $vg/$lv1
+not lvreduce -l-100%FREE $vg/$lv1
+not lvreduce -r -f -l-100%FREE $vg/$lv1
"$FSCK" -n "$lvdev"
# size should remain the same
-lvextend -r -f -l+100%FREE $vg/$lv1
+# lvresize fails with same result with or without -r
+not lvextend -l+100%FREE $vg/$lv1
+not lvextend -r -f -l+100%FREE $vg/$lv1
"$FSCK" -n "$lvdev"
#lvchange -an $vg/$lv1
-lvresize -r -f -l+100%FREE $vg/$lv1
+not lvresize -l+100%FREE $vg/$lv1
+not lvresize -r -f -l+100%FREE $vg/$lv1
"$FSCK" -n "$lvdev"
# Check there is really file system resize happening
@@ -55,7 +59,8 @@ lvresize -r -f -l+100%FREE $vg/$lv1
grep "20000 blocks" out
SIZE=$(get lv_field $vg/$lv1 size)
-lvresize -r -f -l-100%FREE $vg/$lv1
+not lvresize -l-100%FREE $vg/$lv1
+not lvresize -r -f -l-100%FREE $vg/$lv1
test "$SIZE" = "$(get lv_field $vg/$lv1 size)"
"$FSCK" -n "$lvdev" | tee out
diff --git a/tools/command-lines.in b/tools/command-lines.in
index 6e9e7bdea..0cb9425c1 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -1382,7 +1382,7 @@ OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number,
--stripes Number, --stripesize SizeKB, --poolmetadatasize PSizeMB,
--type SegType
OP: PV ...
-ID: lvextend_by_size
+ID: lvextend_size
DESC: Extend an LV by a specified size.
lvextend LV PV ...
@@ -1390,25 +1390,25 @@ OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number,
--nofsck, --nosync, --noudevsync,
--reportformat ReportFmt, --resizefs, --stripes Number, --stripesize SizeKB,
--type SegType
-ID: lvextend_by_pv
+ID: lvextend_pv
DESC: Extend an LV by specified PV extents.
-lvextend --poolmetadatasize PSizeMB LV_thinpool
+lvextend --poolmetadatasize PSizeMB LV_thinpool_linear
OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number,
--nofsck, --nosync, --noudevsync,
--reportformat ReportFmt, --stripes Number, --stripesize SizeKB,
--type SegType
OP: PV ...
-ID: lvextend_pool_metadata_by_size
+ID: lvextend_pool_metadata
DESC: Extend a pool metadata SubLV by a specified size.
-lvextend --usepolicies LV_thinpool_snapshot
+lvextend --usepolicies LV_snapshot_thinpool_vdopool
OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number,
--nofsck, --nosync, --noudevsync,
--reportformat ReportFmt, --resizefs,
--type SegType
OP: PV ...
-ID: lvextend_by_policy
+ID: lvextend_policy
DESC: Extend an LV according to a predefined policy.
---
@@ -1456,7 +1456,7 @@ DESC: Remove the devices file entry for the given PVID.
lvreduce --size NSizeMB LV
OO: --autobackup Bool, --force, --nofsck, --noudevsync,
--reportformat ReportFmt, --resizefs
-ID: lvreduce_general
+ID: lvreduce_size
---
@@ -1487,7 +1487,7 @@ OO: --alloc Alloc, --autobackup Bool, --force,
--stripes Number, --stripesize SizeKB, --poolmetadatasize PSizeMB,
--type SegType
OP: PV ...
-ID: lvresize_by_size
+ID: lvresize_size
DESC: Resize an LV by a specified size.
lvresize LV PV ...
@@ -1495,7 +1495,7 @@ OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat ReportFmt, --resizefs, --stripes Number, --stripesize SizeKB,
--type SegType
-ID: lvresize_by_pv
+ID: lvresize_pv
DESC: Resize an LV by specified PV extents.
lvresize --poolmetadatasize PSizeMB LV_thinpool
@@ -1504,7 +1504,7 @@ OO: --alloc Alloc, --autobackup Bool, --force,
--reportformat ReportFmt, --stripes Number, --stripesize SizeKB,
--type SegType
OP: PV ...
-ID: lvresize_pool_metadata_by_size
+ID: lvresize_pool_metadata
DESC: Resize a pool metadata SubLV by a specified size.
---
diff --git a/tools/lvextend.c b/tools/lvextend.c
index 865bb965e..e915a95cf 100644
--- a/tools/lvextend.c
+++ b/tools/lvextend.c
@@ -17,5 +17,5 @@
int lvextend(struct cmd_context *cmd, int argc, char **argv)
{
- return lvresize(cmd, argc, argv);
+ return lvresize_cmd(cmd, argc, argv);
}
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index ba3ca220b..a5bb6a5c5 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -165,6 +165,16 @@ static const struct command_function _command_functions[CMD_COUNT] = {
{ pvscan_display_CMD, pvscan_display_cmd },
{ pvscan_cache_CMD, pvscan_cache_cmd },
+
+ /* lvextend/lvreduce/lvresize */
+ { lvextend_policy_CMD, lvextend_policy_cmd },
+ { lvextend_pool_metadata_CMD, lvresize_cmd },
+ { lvresize_pool_metadata_CMD, lvresize_cmd },
+ { lvextend_pv_CMD, lvresize_cmd },
+ { lvresize_pv_CMD, lvresize_cmd },
+ { lvextend_size_CMD, lvresize_cmd },
+ { lvreduce_size_CMD, lvresize_cmd },
+ { lvresize_size_CMD, lvresize_cmd },
};
@@ -3153,6 +3163,9 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
if (!(cmd->command = _find_command(cmd, cmd->name, &argc, argv)))
return EINVALID_CMD_LINE;
+ /* avoid this by letting lib code use cmd->command */
+ cmd->command_enum = cmd->command->command_enum;
+
/*
* If option --foo is set which is listed in IO (ignore option) in
* command-lines.in, then unset foo. Commands won't usually use an
diff --git a/tools/lvreduce.c b/tools/lvreduce.c
index 7b5d1583a..721d7f1b9 100644
--- a/tools/lvreduce.c
+++ b/tools/lvreduce.c
@@ -17,5 +17,5 @@
int lvreduce(struct cmd_context *cmd, int argc, char **argv)
{
- return lvresize(cmd, argc, argv);
+ return lvresize_cmd(cmd, argc, argv);
}
diff --git a/tools/lvresize.c b/tools/lvresize.c
index f39f03a40..a7148e52c 100644
--- a/tools/lvresize.c
+++ b/tools/lvresize.c
@@ -15,119 +15,110 @@
#include "tools.h"
-static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
- struct lvresize_params *lp)
+static int _lvresize_params(struct cmd_context *cmd, struct lvresize_params *lp)
{
- const char *cmd_name = command_name(cmd);
const char *type_str = arg_str_value(cmd, type_ARG, NULL);
int only_linear = 0;
+ int set_extents_and_size = 0;
- if (type_str) {
- if (!strcmp(type_str, "linear")) {
- type_str = "striped";
- only_linear = 1; /* User requested linear only target */
- }
+ memset(lp, 0, sizeof(struct lvresize_params));
- if (!(lp->segtype = get_segtype_from_string(cmd, type_str)))
- return_0;
- }
+ switch (cmd->command->command_enum) {
+ case lvextend_policy_CMD:
+ lp->resize = LV_EXTEND;
+ lp->size = 0;
+ lp->extents = 0;
+ lp->percent = PERCENT_LV;
+ lp->sign = SIGN_PLUS;
+ lp->poolmetadata_size = 0;
+ lp->use_policies = 1;
+ break;
+
+ case lvextend_pool_metadata_CMD:
+ case lvresize_pool_metadata_CMD:
+ lp->resize = LV_EXTEND;
+ lp->size = 0;
+ lp->extents = 0;
+ lp->percent = PERCENT_NONE;
+ lp->sign = SIGN_NONE;
+ lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0);
+ lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
+ break;
+
+ case lvextend_pv_CMD:
+ case lvresize_pv_CMD:
+ lp->resize = LV_EXTEND;
+ lp->size = 0;
+ lp->extents = 0;
+ lp->percent_value = 100;
+ lp->percent = PERCENT_PVS;
+ lp->sign = SIGN_PLUS;
+ lp->poolmetadata_size = 0;
+ lp->resizefs = arg_is_set(cmd, resizefs_ARG);
+ lp->nofsck = arg_is_set(cmd, nofsck_ARG);
+ break;
+
+ case lvextend_size_CMD:
+ lp->resize = LV_EXTEND;
+ lp->resizefs = arg_is_set(cmd, resizefs_ARG);
+ lp->nofsck = arg_is_set(cmd, nofsck_ARG);
+ if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0)))
+ lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
+ set_extents_and_size = 1;
+ break;
- if (!strcmp(cmd_name, "lvreduce"))
+ case lvreduce_size_CMD:
lp->resize = LV_REDUCE;
- else if (!strcmp(cmd_name, "lvextend"))
- lp->resize = LV_EXTEND;
- else
+ lp->poolmetadata_size = 0;
+ lp->resizefs = arg_is_set(cmd, resizefs_ARG);
+ lp->nofsck = arg_is_set(cmd, nofsck_ARG);
+ set_extents_and_size = 1;
+ break;
+
+ case lvresize_size_CMD:
lp->resize = LV_ANY;
+ lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0);
+ lp->resizefs = arg_is_set(cmd, resizefs_ARG);
+ lp->nofsck = arg_is_set(cmd, nofsck_ARG);
+ if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0)))
+ lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
+ set_extents_and_size = 1;
+ break;
- lp->sign = lp->poolmetadata_sign = SIGN_NONE;
-
- if ((lp->use_policies = arg_is_set(cmd, usepolicies_ARG))) {
- /* do nothing; lv_resize will handle --use-policies itself */
- if (arg_from_list_is_set(cmd, NULL,
- chunksize_ARG, extents_ARG,
- poolmetadatasize_ARG,
- regionsize_ARG,
- size_ARG,
- stripes_ARG, stripesize_ARG,
- -1))
- log_print_unless_silent("Ignoring size parameters with --use-policies.");
- } else {
- /*
- * Allow omission of extents and size if the user has given us
- * one or more PVs. Most likely, the intent was "resize this
- * LV the best you can with these PVs"
- * If only --poolmetadatasize is specified with list of PVs,
- * then metadata will be extended there.
- */
+ default:
+ log_error(INTERNAL_ERROR "unknown lvresize type");
+ return 0;
+ };
+
+ if (set_extents_and_size) {
if ((lp->extents = arg_uint_value(cmd, extents_ARG, 0))) {
- lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
+ lp->sign = arg_sign_value(cmd, extents_ARG, 0);
lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
}
-
- if (arg_from_list_is_zero(cmd, "may not be zero",
- chunksize_ARG, extents_ARG,
- poolmetadatasize_ARG,
- regionsize_ARG,
- size_ARG,
- stripes_ARG, stripesize_ARG,
- virtualsize_ARG,
- -1))
- return_0;
-
- if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0))) {
- lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
- if (lp->poolmetadata_sign == SIGN_MINUS) {
- log_error("Can't reduce pool metadata size.");
- return 0;
- }
- }
-
if ((lp->size = arg_uint64_value(cmd, size_ARG, 0))) {
- lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
+ lp->sign = arg_sign_value(cmd, size_ARG, 0);
lp->percent = PERCENT_NONE;
}
-
if (lp->size && lp->extents) {
log_error("Please specify either size or extents but not both.");
return 0;
}
-
- if (!lp->extents &&
- !lp->size &&
- !lp->poolmetadata_size &&
- (argc >= 2)) {
- lp->extents = 100;
- lp->percent = PERCENT_PVS;
- lp->sign = SIGN_PLUS;
- }
}
- if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
- log_error("Negative argument not permitted - use lvreduce.");
- return 0;
- }
+ lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, 0);
+ lp->yes = arg_is_set(cmd, yes_ARG);
+ lp->force = arg_is_set(cmd, force_ARG),
+ lp->nosync = arg_is_set(cmd, nosync_ARG);
+ lp->lockopt = arg_str_value(cmd, lockopt_ARG, NULL);
- if (lp->resize == LV_REDUCE &&
- ((lp->sign == SIGN_PLUS) ||
- (lp->poolmetadata_sign == SIGN_PLUS))) {
- log_error("Positive sign not permitted - use lvextend.");
- return 0;
- }
-
- if (!argc) {
- log_error("Please provide the logical volume name.");
- return 0;
- }
-
- lp->lv_name = argv[0];
-
- if (!validate_lvname_param(cmd, &lp->vg_name, &lp->lv_name))
- return_0;
+ if (type_str) {
+ if (!strcmp(type_str, "linear")) {
+ type_str = "striped";
+ only_linear = 1; /* User requested linear only target */
+ }
- /* Check for $LVM_VG_NAME */
- if (!lp->vg_name && !(lp->vg_name = extract_vgname(cmd, NULL))) {
- log_error("Please specify a logical volume path.");
- return 0;
+ if (!(lp->segtype = get_segtype_from_string(cmd, type_str)))
+ return_0;
}
if (arg_is_set(cmd, mirrors_ARG)) {
@@ -156,66 +147,150 @@ static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
return 0;
}
- lp->argc = --argc;
- lp->argv = ++argv;
-
return 1;
}
-static int _lvresize_single(struct cmd_context *cmd, const char *vg_name,
- struct volume_group *vg, struct processing_handle *handle)
+/*
+ * lvextend --use-policies is usually called by dmeventd, as a method of
+ * "auto extending" an LV as it's used. It checks how full a snapshot cow or
+ * thin pool is, and extends it if it's too full, based on threshold settings
+ * in lvm.conf for when to auto extend it.
+ *
+ * The extension of a thin pool LV can involve extending either the data sub
+ * LV, the metadata sub LV, or both, so there may be two LVs extended here.
+ */
+static int _lv_extend_policy(struct cmd_context *cmd, struct logical_volume *lv,
+ struct lvresize_params *lp, int *skipped)
{
- struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle;
- struct dm_list *pvh;
- struct logical_volume *lv;
- int ret = ECMD_FAILED;
-
- /* Does LV exist? */
- if (!(lv = find_lv(vg, lp->lv_name))) {
- log_error("Logical volume %s not found in volume group %s.",
- lp->lv_name, vg->name);
- goto out;
+ struct lvresize_params lp_meta;
+ uint32_t percent_main = 0;
+ uint32_t percent_meta = 0;
+ int is_active;
+
+ memset(&lp_meta, 0, sizeof(lp_meta));
+
+ if (!lv_is_cow(lv) && !lv_is_thin_pool(lv) && !lv_is_vdo_pool(lv)) {
+ log_error("lvextend policy is supported only for snapshot, thin pool and vdo pool volumes.");
+ *skipped = 1;
+ return 0;
+ }
+
+ is_active = lv_is_active(lv);
+
+ if (vg_is_shared(lv->vg) && !is_active) {
+ log_debug("lvextend policy requires LV to be active in a shared VG.");
+ *skipped = 1;
+ return 1;
+ }
+
+ if (lv_is_thin_pool(lv) && !is_active) {
+ log_error("lvextend using policy requires the thin pool to be active.");
+ return 0;
}
- if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc, lp->argv, 1) : &vg->pvs))
- goto_out;
+ /*
+ * Calculate the percent of extents to extend the LV based on current
+ * usage info from the kernel and policy settings from lvm.conf, e.g.
+ * autoextend_threshold, autoextend_percent. For thin pools, both the
+ * thin pool data LV and thin pool metadata LV may need to be extended.
+ * In this case, percent_main is the amount to extend the data LV, and
+ * percent_meta is the amount to extend the metadata LV.
+ */
+ if (!lv_extend_policy_calculate_percent(lv, &percent_main, &percent_meta))
+ return_0;
+
+ if (!percent_main && !percent_meta) {
+ log_debug("lvextend policy not needed.");
+ *skipped = 1;
+ return 1;
+ }
- if (!lv_resize(lv, lp, pvh))
- goto_out;
+ *skipped = 0;
+ lp->policy_percent_main = percent_main;
+ lp->policy_percent_meta = percent_meta;
+
+ return lv_resize(cmd, lv, lp);
+}
+
+static int _lvextend_policy_single(struct cmd_context *cmd, struct logical_volume *lv,
+ struct processing_handle *handle)
+{
+ struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle;
+ int skipped = 0;
+
+ if (cmd->position_argc > 1) {
+ /* First pos arg is required LV, remaining are optional PVs. */
+ if (!(lp->pvh = create_pv_list(cmd->mem, lv->vg, cmd->position_argc - 1, cmd->position_argv + 1, 0)))
+ return_ECMD_FAILED;
+ } else
+ lp->pvh = &lv->vg->pvs;
+
+ if (!_lv_extend_policy(cmd, lv, lp, &skipped))
+ return ECMD_FAILED;
+
+ if (!skipped)
+ log_print_unless_silent("Logical volume %s successfully resized.", display_lvname(lv));
+ return ECMD_PROCESSED;
+}
+
+static int _lvresize_single(struct cmd_context *cmd, struct logical_volume *lv,
+ struct processing_handle *handle)
+{
+ struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle;
+
+ if (cmd->position_argc > 1) {
+ /* First pos arg is required LV, remaining are optional PVs. */
+ if (!(lp->pvh = create_pv_list(cmd->mem, lv->vg, cmd->position_argc - 1, cmd->position_argv + 1, 0)))
+ return_ECMD_FAILED;
+ } else
+ lp->pvh = &lv->vg->pvs;
+
+ if (!lv_resize(cmd, lv, lp))
+ return ECMD_FAILED;
+
+ log_print_unless_silent("Logical volume %s successfully resized.",
+ display_lvname(lv));
+ return ECMD_PROCESSED;
+}
+
+int lvextend_policy_cmd(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct processing_handle *handle;
+ struct lvresize_params lp;
+ int ret;
+
+ if (!_lvresize_params(cmd, &lp))
+ return EINVALID_CMD_LINE;
+
+ if (!(handle = init_processing_handle(cmd, NULL)))
+ return ECMD_FAILED;
+
+ handle->custom_handle = &lp;
+
+ ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE,
+ handle, NULL, &_lvextend_policy_single);
+
+ destroy_processing_handle(cmd, handle);
- ret = ECMD_PROCESSED;
-out:
return ret;
}
-int lvresize(struct cmd_context *cmd, int argc, char **argv)
+int lvresize_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
- struct lvresize_params lp = {
- .alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, 0),
- .yes = arg_is_set(cmd, yes_ARG),
- .force = arg_is_set(cmd, force_ARG),
- .nofsck = arg_is_set(cmd, nofsck_ARG),
- .nosync = arg_is_set(cmd, nosync_ARG),
- .resizefs = arg_is_set(cmd, resizefs_ARG),
- .lockopt = arg_str_value(cmd, lockopt_ARG, NULL),
- };
+ struct lvresize_params lp;
int ret;
- if (!_lvresize_params(cmd, argc, argv, &lp)) {
- stack;
+ if (!_lvresize_params(cmd, &lp))
return EINVALID_CMD_LINE;
- }
- if (!(handle = init_processing_handle(cmd, NULL))) {
- log_error("Failed to initialize processing handle.");
+ if (!(handle = init_processing_handle(cmd, NULL)))
return ECMD_FAILED;
- }
handle->custom_handle = &lp;
- ret = process_each_vg(cmd, 0, NULL, lp.vg_name, NULL, READ_FOR_UPDATE, 0, handle,
- &_lvresize_single);
+ ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE,
+ handle, NULL, &_lvresize_single);
destroy_processing_handle(cmd, handle);
@@ -224,3 +299,16 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv)
return ret;
}
+
+/*
+ * All lvresize command defs have their own function,
+ * so the generic function name is unused.
+ */
+
+int lvresize(struct cmd_context *cmd, int argc, char **argv)
+{
+ log_error(INTERNAL_ERROR "Missing function for command definition %d:%s.",
+ cmd->command->command_index, cmd->command->command_id);
+ return ECMD_FAILED;
+}
+
diff --git a/tools/tools.h b/tools/tools.h
index 2636c22da..36da3bc7e 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -45,19 +45,18 @@
#include "lib/notify/lvmnotify.h"
#include "lib/label/hints.h"
+/*
+ * cmd_enum.h uses the generated cmds.h to create the enum with an ID
+ * for each command definition in command-lines.in.
+ */
+#include "lib/commands/cmd_enum.h"
+
#include <ctype.h>
#include <sys/types.h>
#define CMD_LEN 256
#define MAX_ARGS 64
-/* define the enums for each unique ID in command defs in command-lines.in */
-enum {
-#define cmd(a, b) a ,
-#include "cmds.h"
-#undef cmd
-};
-
/* define the enums for the values accepted by command line --options, foo_VAL */
enum {
#define val(a, b, c, d) a ,
@@ -296,4 +295,7 @@ int lvconvert_cachevol_attach_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle);
+int lvresize_cmd(struct cmd_context *cmd, int argc, char **argv);
+int lvextend_policy_cmd(struct cmd_context *cmd, int argc, char **argv);
+
#endif