All of lore.kernel.org
 help / color / mirror / Atom feed
* main - lvresize: restructure code
@ 2022-09-13 20:18 David Teigland
  0 siblings, 0 replies; only message in thread
From: David Teigland @ 2022-09-13 20:18 UTC (permalink / raw)
  To: lvm-devel

Gitweb:        https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=18722dfdf4d3e6f172d0b2af8bbdc4a154ea1dc0
Commit:        18722dfdf4d3e6f172d0b2af8bbdc4a154ea1dc0
Parent:        55e9494e5fe3e3d2790fca6330b848a577e0b1c0
Author:        David Teigland <teigland@redhat.com>
AuthorDate:    Fri Sep 9 16:07:07 2022 -0500
Committer:     David Teigland <teigland@redhat.com>
CommitterDate: Fri Sep 9 16:18:55 2022 -0500

lvresize: restructure code

Rewrite top level resize function to prepare for adding
new fs resizing.
---
 lib/commands/cmd_enum.h          |  19 +
 lib/commands/toolcontext.h       |   2 +
 lib/locking/lvmlockd.c           |   6 +-
 lib/metadata/lv_manip.c          | 942 +++++++++++++++++++++++----------------
 lib/metadata/metadata-exported.h |  39 +-
 lib/metadata/metadata.c          |  12 +
 lib/metadata/pool_manip.c        |   3 +
 test/shell/lvextend-thin-full.sh |  11 +-
 test/shell/lvresize-full.sh      |  13 +-
 tools/command-lines.in           |  20 +-
 tools/lvextend.c                 |   2 +-
 tools/lvmcmdline.c               |  13 +
 tools/lvreduce.c                 |   2 +-
 tools/lvresize.c                 | 346 ++++++++------
 tools/tools.h                    |  16 +-
 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


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2022-09-13 20:18 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-09-13 20:18 main - lvresize: restructure code David Teigland

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.