[lvm-devel] master - lvconvert: remove unused code

David Teigland teigland at fedoraproject.org
Mon Feb 13 18:10:22 UTC 2017


Gitweb:        http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=041c2fef88ab2e546555625039a17ccbb7b1c9c5
Commit:        041c2fef88ab2e546555625039a17ccbb7b1c9c5
Parent:        46b2599d5c4e01eee66be7c45aa6cf36d7cbc50f
Author:        David Teigland <teigland at redhat.com>
AuthorDate:    Tue Dec 20 15:17:48 2016 -0600
Committer:     David Teigland <teigland at redhat.com>
CommitterDate: Mon Feb 13 08:20:10 2017 -0600

lvconvert: remove unused code

---
 tools/lvconvert.c | 3170 +++++++++++++++--------------------------------------
 1 files changed, 882 insertions(+), 2288 deletions(-)

diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 642d6b7..757e8b4 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -19,25 +19,6 @@
 #include "lvconvert_poll.h"
 #include "command-lines-count.h"
 
-/*
- * Guidelines for mapping options to operations.
- *
- * There should be a clear and unique correspondence between an option
- * name and the operation to be performed.
- *
- * An option with a given name should always perform the same operation.
- * If the same operation applies to two types of LV, then the same option
- * name can be used with both LV types.  But, a given option name should
- * not be used to perform different operations depending on the LV type it
- * is used with.
- *
- * --merge and --split are examples where a single option name has been
- * overloaded with different operations.  The case of --split has been
- * corrected with the clear and unique variations --splitcache,
- * --splitsnapshot, --splitmirror, which should allow --split to be
- * deprecated.  (The same is still needed for --merge.)
- */
-
 typedef enum {
 	/* Split:
 	 *   For a mirrored or raid LV, split mirror into two mirrors, optionally tracking
@@ -52,23 +33,13 @@ typedef enum {
 
 struct lvconvert_params {
 	/* Exactly one of these 12 command categories is determined */
-	int merge;		/* 1 */
-	int split;		/* 2 */
-	int splitsnapshot;	/* 3 */
-	int splitcache;		/* 4 */
-	int keep_mimages;	/* 5 */	/* --splitmirrors */
-	int cache;		/* 6 */
-	int uncache;		/* 7 */
-	int snapshot;		/* 8 */
-	int thin;		/* 11 */
-	/* other */		/* 12 */
+	int split;		/* 1 */
+	int keep_mimages;	/* 2 */	/* --splitmirrors */
+	/* other */		/* 3 */
 
 	/* FIXME Eliminate all cases where more than one of the above are set then use conv_type instead */
 	conversion_type_t	conv_type;
 
-	int merge_snapshot;	/* CONV_MERGE is set */
-	int merge_mirror;	/* CONV_MERGE is set */
-
 	int track_changes;	/* CONV_SPLIT_MIRRORS is set */
 
 	int corelog;		/* Equivalent to --mirrorlog core */
@@ -80,7 +51,6 @@ struct lvconvert_params {
 
 	const struct segment_type *segtype;	/* Holds what segment type you will get */
 
-	int poolmetadataspare;
 	int force;
 	int yes;
 	int zero;
@@ -92,8 +62,6 @@ struct lvconvert_params {
 	int wait_completion;
 	int need_polling;
 
-	int thin_chunk_size_calc_policy;
-	uint32_t chunk_size;
 	uint32_t region_size;
 
 	uint32_t mirrors;
@@ -103,9 +71,6 @@ struct lvconvert_params {
 	unsigned stripes_supplied;
 	unsigned stripe_size_supplied;
 	uint32_t read_ahead;
-	cache_mode_t cache_mode; /* cache */
-	const char *policy_name; /* cache */
-	struct dm_config_tree *policy_settings; /* cache */
 
 	unsigned target_attr;
 
@@ -118,15 +83,7 @@ struct lvconvert_params {
 	struct logical_volume *lv_to_poll;
 	struct dm_list idls;
 
-	uint32_t pool_metadata_extents;
-	int passed_args;
-	uint64_t pool_metadata_size;
 	const char *origin_name;
-	const char *pool_data_name;
-	struct logical_volume *pool_data_lv;
-	const char *pool_metadata_name;
-	struct logical_volume *pool_metadata_lv;
-	thin_discards_t discards;
 };
 
 struct convert_poll_id_list {
@@ -145,12 +102,6 @@ static void _set_conv_type(struct lvconvert_params *lp, int conv_type)
 	lp->conv_type = conv_type;
 }
 
-/* -s/--snapshot and --type snapshot are synonyms */
-static int _snapshot_type_requested(struct cmd_context *cmd, const char *type_str)
-{
-	return (arg_is_set(cmd, snapshot_ARG) || !strcmp(type_str, SEG_TYPE_NAME_SNAPSHOT));
-}
-
 static int _raid0_type_requested(const char *type_str)
 {
 	return (!strcmp(type_str, SEG_TYPE_NAME_RAID0) || !strcmp(type_str, SEG_TYPE_NAME_RAID0_META));
@@ -1282,11 +1233,6 @@ static int _lvconvert_mirrors(struct cmd_context *cmd,
 		return 0;
 	}
 
-	if (lp->merge_mirror) {
-		log_error("Unable to merge mirror images of segment type 'mirror'.");
-		return 0;
-	}
-
 	if (!_lvconvert_validate_thin(lv, lp))
 		return_0;
 
@@ -1424,7 +1370,7 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l
 	if (!_is_valid_raid_conversion(seg->segtype, lp->segtype))
 		goto try_new_takeover_or_reshape;
 
-	if (seg_is_linear(seg) && !lp->merge_mirror && !lp->mirrors_supplied) {
+	if (seg_is_linear(seg) && !lp->mirrors_supplied) {
 		if (_raid0_type_requested(lp->type_str)) {
 			log_error("Linear LV %s cannot be converted to %s.",
 				  display_lvname(lv), lp->type_str);
@@ -1466,9 +1412,6 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l
 		return 0;
 	}
 
-	if (lp->merge_mirror)
-		return lv_raid_merge(lv);
-
 	if (lp->track_changes)
 		return lv_raid_split_and_track(lv, lp->pvh);
 
@@ -1563,97 +1506,413 @@ try_new_takeover_or_reshape:
 	return 0;
 }
 
-static int _lvconvert_splitsnapshot(struct cmd_context *cmd, struct logical_volume *cow)
+/*
+ * Functions called to perform a specific operation on a specific LV type.
+ *
+ * _convert_<lvtype>_<operation>
+ *
+ * For cases where an operation does not apply to the LV itself, but
+ * is implicitly redirected to a sub-LV, these functions locate the
+ * correct sub-LV and call the operation on that sub-LV.  If a sub-LV
+ * of the proper type is not found, these functions report the error.
+ *
+ * FIXME: the _lvconvert_foo() functions can be cleaned up since they
+ * are now only called for valid combinations of LV type and operation.
+ * After that happens, the code remaining in those functions can be
+ * moved into the _convert_lvtype_operation() functions below.
+ */
+
+/*
+ * Change the number of images in a mirror LV.
+ * lvconvert --mirrors Number LV
+ */
+static int _convert_mirror_number(struct cmd_context *cmd, struct logical_volume *lv,
+				  struct lvconvert_params *lp)
 {
-	struct volume_group *vg = cow->vg;
-	const char *cow_name = display_lvname(cow);
+	return _lvconvert_mirrors(cmd, lv, lp);
+}
 
-	if (lv_is_virtual_origin(origin_from_cow(cow))) {
-		log_error("Unable to split off snapshot %s with virtual origin.", cow_name);
-		return 0;
-	}
+/*
+ * Split images from a mirror LV and use them to create a new LV.
+ * lvconvert --splitmirrors Number LV
+ *
+ * Required options:
+ * --name Name
+ */
 
-	if (!(vg->fid->fmt->features & FMT_MDAS)) {
-		log_error("Unable to split off snapshot %s using old LVM1-style metadata.", cow_name);
-		return 0;
-	}
+static int _convert_mirror_splitmirrors(struct cmd_context *cmd, struct logical_volume *lv,
+					struct lvconvert_params *lp)
+{
+	return _lvconvert_mirrors(cmd, lv, lp);
+}
 
-	if (is_lockd_type(vg->lock_type)) {
-		/* FIXME: we need to create a lock for the new LV. */
-		log_error("Unable to split snapshots in VG with lock_type %s.", vg->lock_type);
-		return 0;
-	}
+/*
+ * Change the type of log used by a mirror LV.
+ * lvconvert --mirrorlog Type LV
+ */
+static int _convert_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
+				  struct lvconvert_params *lp)
+{
+	return _lvconvert_mirrors(cmd, lv, lp);
+}
 
-	if (lv_is_active_locally(cow)) {
-		if (!lv_check_not_in_use(cow, 1))
-			return_0;
+/*
+ * Convert mirror LV to linear LV.
+ * lvconvert --type linear LV
+ *
+ * Alternate syntax:
+ * lvconvert --mirrors 0 LV
+ */
+static int _convert_mirror_linear(struct cmd_context *cmd, struct logical_volume *lv,
+				  struct lvconvert_params *lp)
+{
+	return _lvconvert_mirrors(cmd, lv, lp);
+}
 
-		if ((arg_count(cmd, force_ARG) == PROMPT) &&
-		    !arg_count(cmd, yes_ARG) &&
-		    lv_is_visible(cow) &&
-		    lv_is_active(cow)) {
-			if (yes_no_prompt("Do you really want to split off active "
-					  "logical volume %s? [y/n]: ", display_lvname(cow)) == 'n') {
-				log_error("Logical volume %s not split.", display_lvname(cow));
-				return 0;
-			}
-		}
-	}
+/*
+ * Convert mirror LV to raid1 LV.
+ * lvconvert --type raid1 LV
+ */
+static int _convert_mirror_raid(struct cmd_context *cmd, struct logical_volume *lv,
+				struct lvconvert_params *lp)
+{
+	return _lvconvert_raid(lv, lp);
+}
 
-	if (!archive(vg))
-		return_0;
+/*
+ * Change the number of images in a raid1 LV.
+ * lvconvert --mirrors Number LV
+ */
+static int _convert_raid_number(struct cmd_context *cmd, struct logical_volume *lv,
+				struct lvconvert_params *lp)
+{
+	return _lvconvert_raid(lv, lp);
+}
 
-	log_verbose("Splitting snapshot %s from its origin.", display_lvname(cow));
+/*
+ * Split images from a raid1 LV and use them to create a new LV.
+ * lvconvert --splitmirrors Number LV
+ *
+ * Required options:
+ * --trackchanges | --name Name
+ */
+static int _convert_raid_splitmirrors(struct cmd_context *cmd, struct logical_volume *lv,
+				      struct lvconvert_params *lp)
+{
+	/* FIXME: split the splitmirrors section out of _lvconvert_raid and call it here. */
+	return _lvconvert_raid(lv, lp);
+}
 
-	if (!vg_remove_snapshot(cow))
-		return_0;
+/*
+ * Convert a raid* LV to use a different raid level.
+ * lvconvert --type raid* LV
+ */
+static int _convert_raid_raid(struct cmd_context *cmd, struct logical_volume *lv,
+			      struct lvconvert_params *lp)
+{
+	return _lvconvert_raid(lv, lp);
+}
 
-	backup(vg);
+/*
+ * Convert a raid* LV to a mirror LV.
+ * lvconvert --type mirror LV
+ */
+static int _convert_raid_mirror(struct cmd_context *cmd, struct logical_volume *lv,
+			      struct lvconvert_params *lp)
+{
+	return _lvconvert_raid(lv, lp);
+}
 
-	log_print_unless_silent("Logical Volume %s split from its origin.", display_lvname(cow));
+/*
+ * Convert a raid* LV to a striped LV.
+ * lvconvert --type striped LV
+ */
+static int _convert_raid_striped(struct cmd_context *cmd, struct logical_volume *lv,
+				 struct lvconvert_params *lp)
+{
+	return _lvconvert_raid(lv, lp);
+}
 
-	return 1;
+/*
+ * Convert a raid* LV to a linear LV.
+ * lvconvert --type linear LV
+ */
+static int _convert_raid_linear(struct cmd_context *cmd, struct logical_volume *lv,
+				struct lvconvert_params *lp)
+{
+	return _lvconvert_raid(lv, lp);
 }
 
-static int _lvconvert_split_and_keep_cachepool(struct cmd_context *cmd,
-				   struct logical_volume *lv,
-				   struct logical_volume *cachepool_lv)
+/*
+ * Convert a striped/linear LV to a mirror LV.
+ * lvconvert --type mirror LV
+ *
+ * Required options:
+ * --mirrors Number
+ *
+ * Alternate syntax:
+ * This is equivalent to above when global/mirror_segtype_default="mirror".
+ * lvconvert --mirrors Number LV
+ */
+static int _convert_striped_mirror(struct cmd_context *cmd, struct logical_volume *lv,
+				   struct lvconvert_params *lp)
 {
-	log_debug("Detaching cache pool %s from cache LV %s.",
-		  display_lvname(cachepool_lv), display_lvname(lv));
+	return _lvconvert_mirrors(cmd, lv, lp);
+}
 
-	if (!archive(lv->vg))
-		return_0;
+/*
+ * Convert a striped/linear LV to a raid* LV.
+ * lvconvert --type raid* LV
+ *
+ * Required options:
+ * --mirrors Number
+ *
+ * Alternate syntax:
+ * This is equivalent to above when global/mirror_segtype_default="raid1".
+ * lvconvert --mirrors Number LV
+ */
+static int _convert_striped_raid(struct cmd_context *cmd, struct logical_volume *lv,
+				 struct lvconvert_params *lp)
+{
+	return _lvconvert_raid(lv, lp);
+}
 
-	if (!lv_cache_remove(lv))
-		return_0;
+static int _convert_mirror(struct cmd_context *cmd, struct logical_volume *lv,
+			   struct lvconvert_params *lp)
+{
+	if (arg_is_set(cmd, mirrors_ARG))
+		return _convert_mirror_number(cmd, lv, lp);
 
-	if (!vg_write(lv->vg) || !vg_commit(lv->vg))
-		return_0;
+	if (arg_is_set(cmd, splitmirrors_ARG))
+		return _convert_mirror_splitmirrors(cmd, lv, lp);
 
-	backup(lv->vg);
+	if (arg_is_set(cmd, mirrorlog_ARG) || arg_is_set(cmd, corelog_ARG))
+		return _convert_mirror_log(cmd, lv, lp);
 
-	log_print_unless_silent("Logical volume %s is not cached and cache pool %s is unused.",
-				display_lvname(lv), display_lvname(cachepool_lv));
+	if (_linear_type_requested(lp->type_str))
+		return _convert_mirror_linear(cmd, lv, lp);
 
-	return 1;
+	if (segtype_is_raid(lp->segtype))
+		return _convert_mirror_raid(cmd, lv, lp);
+
+	log_error("Unknown operation on mirror LV %s.", display_lvname(lv));
+	return 0;
 }
 
-static int _lvconvert_split_and_remove_cachepool(struct cmd_context *cmd,
-				   struct logical_volume *lv,
-				   struct logical_volume *cachepool_lv)
+static int _convert_raid(struct cmd_context *cmd, struct logical_volume *lv,
+			 struct lvconvert_params *lp)
 {
-	struct lv_segment *seg;
-	struct logical_volume *remove_lv;
+	if (arg_is_set(cmd, mirrors_ARG))
+		return _convert_raid_number(cmd, lv, lp);
 
-	seg = first_seg(lv);
+	if (arg_is_set(cmd, splitmirrors_ARG))
+		return _convert_raid_splitmirrors(cmd, lv, lp);
 
-	if (lv_is_partial(seg_lv(seg, 0))) {
-		log_warn("WARNING: Cache origin logical volume %s is missing.",
-			 display_lvname(seg_lv(seg, 0)));
-		remove_lv = lv; /* When origin is missing, drop everything */
-	} else
-		remove_lv = seg->pool_lv;
+	if (segtype_is_raid(lp->segtype))
+		return _convert_raid_raid(cmd, lv, lp);
+
+	if (segtype_is_mirror(lp->segtype))
+		return _convert_raid_mirror(cmd, lv, lp);
+
+	if (!strcmp(lp->type_str, SEG_TYPE_NAME_STRIPED))
+		return _convert_raid_striped(cmd, lv, lp);
+
+	if (_linear_type_requested(lp->type_str))
+		return _convert_raid_linear(cmd, lv, lp);
+
+	log_error("Unknown operation on raid LV %s.", display_lvname(lv));
+	return 0;
+}
+
+static int _convert_striped(struct cmd_context *cmd, struct logical_volume *lv,
+			    struct lvconvert_params *lp)
+{
+	const char *mirrors_type = find_config_tree_str(cmd, global_mirror_segtype_default_CFG, NULL);
+
+	if (!strcmp(lp->type_str, SEG_TYPE_NAME_MIRROR))
+		return _convert_striped_mirror(cmd, lv, lp);
+
+	if (segtype_is_raid(lp->segtype))
+		return _convert_striped_raid(cmd, lv, lp);
+
+	/* --mirrors can mean --type mirror or --type raid1 depending on config setting. */
+
+	if (arg_is_set(cmd, mirrors_ARG) && mirrors_type && !strcmp(mirrors_type, SEG_TYPE_NAME_MIRROR))
+		return _convert_striped_mirror(cmd, lv, lp);
+
+	if (arg_is_set(cmd, mirrors_ARG) && mirrors_type && !strcmp(mirrors_type, SEG_TYPE_NAME_RAID1))
+		return _convert_striped_raid(cmd, lv, lp);
+
+	log_error("Unknown operation on striped or linear LV %s.", display_lvname(lv));
+	return 0;
+}
+
+static int _lvconvert_raid_types(struct cmd_context *cmd, struct logical_volume *lv,
+				 struct lvconvert_params *lp)
+{
+	struct lv_segment *seg = first_seg(lv);
+	int ret = 0;
+
+	/* Set up segtype either from type_str or else to match the existing one. */
+	if (!*lp->type_str)
+		lp->segtype = seg->segtype;
+	else if (!(lp->segtype = get_segtype_from_string(cmd, lp->type_str)))
+		goto_out;
+
+	if (!strcmp(lp->type_str, SEG_TYPE_NAME_MIRROR)) {
+		if (!lp->mirrors_supplied && !seg_is_raid1(seg)) {
+			log_error("Conversions to --type mirror require -m/--mirrors");
+			goto out;
+		}
+	}
+
+	/* lv->segtype can't be NULL */
+	if (activation() && lp->segtype->ops->target_present &&
+	    !lp->segtype->ops->target_present(cmd, NULL, &lp->target_attr)) {
+		log_error("%s: Required device-mapper target(s) not "
+			  "detected in your kernel.", lp->segtype->name);
+		goto out;
+	}
+
+	/* Process striping parameters */
+	/* FIXME This is incomplete */
+	if (_mirror_or_raid_type_requested(cmd, lp->type_str) || _raid0_type_requested(lp->type_str) ||
+	    _striped_type_requested(lp->type_str) || lp->mirrorlog || lp->corelog) {
+		/* FIXME Handle +/- adjustments too? */
+		if (!get_stripe_params(cmd, lp->segtype, &lp->stripes, &lp->stripe_size, &lp->stripes_supplied, &lp->stripe_size_supplied))
+			goto_out;
+
+		if (_raid0_type_requested(lp->type_str) || _striped_type_requested(lp->type_str))
+			/* FIXME Shouldn't need to override get_stripe_params which defaults to 1 stripe (i.e. linear)! */
+			/* The default keeps existing number of stripes, handled inside the library code */
+			if (!arg_is_set(cmd, stripes_long_ARG))
+				lp->stripes = 0;
+	}
+
+	if (lv_is_cache(lv))
+		lv = seg_lv(first_seg(lv), 0);
+
+	if (lv_is_mirror(lv)) {
+		ret = _convert_mirror(cmd, lv, lp);
+		goto out;
+	}
+
+	if (lv_is_raid(lv)) {
+		ret = _convert_raid(cmd, lv, lp);
+		goto out;
+	}
+
+	/*
+	 * FIXME: add lv_is_striped() and lv_is_linear()?
+	 * This does not include raid0 which is caught by the test above.
+	 * If operations differ between striped and linear, split this case.
+	 */
+	if (segtype_is_striped(seg->segtype) || segtype_is_linear(seg->segtype)) {
+		ret = _convert_striped(cmd, lv, lp);
+		goto out;
+	}
+
+	/*
+	 * The intention is to explicitly check all cases above and never
+	 * reach here, but this covers anything that was missed.
+	 */
+	log_error("Cannot convert LV %s.", display_lvname(lv));
+
+out:
+	return ret ? ECMD_PROCESSED : ECMD_FAILED;
+}
+
+static int _lvconvert_splitsnapshot(struct cmd_context *cmd, struct logical_volume *cow)
+{
+	struct volume_group *vg = cow->vg;
+	const char *cow_name = display_lvname(cow);
+
+	if (lv_is_virtual_origin(origin_from_cow(cow))) {
+		log_error("Unable to split off snapshot %s with virtual origin.", cow_name);
+		return 0;
+	}
+
+	if (!(vg->fid->fmt->features & FMT_MDAS)) {
+		log_error("Unable to split off snapshot %s using old LVM1-style metadata.", cow_name);
+		return 0;
+	}
+
+	if (is_lockd_type(vg->lock_type)) {
+		/* FIXME: we need to create a lock for the new LV. */
+		log_error("Unable to split snapshots in VG with lock_type %s.", vg->lock_type);
+		return 0;
+	}
+
+	if (lv_is_active_locally(cow)) {
+		if (!lv_check_not_in_use(cow, 1))
+			return_0;
+
+		if ((arg_count(cmd, force_ARG) == PROMPT) &&
+		    !arg_count(cmd, yes_ARG) &&
+		    lv_is_visible(cow) &&
+		    lv_is_active(cow)) {
+			if (yes_no_prompt("Do you really want to split off active "
+					  "logical volume %s? [y/n]: ", display_lvname(cow)) == 'n') {
+				log_error("Logical volume %s not split.", display_lvname(cow));
+				return 0;
+			}
+		}
+	}
+
+	if (!archive(vg))
+		return_0;
+
+	log_verbose("Splitting snapshot %s from its origin.", display_lvname(cow));
+
+	if (!vg_remove_snapshot(cow))
+		return_0;
+
+	backup(vg);
+
+	log_print_unless_silent("Logical Volume %s split from its origin.", display_lvname(cow));
+
+	return 1;
+}
+
+static int _lvconvert_split_and_keep_cachepool(struct cmd_context *cmd,
+				   struct logical_volume *lv,
+				   struct logical_volume *cachepool_lv)
+{
+	log_debug("Detaching cache pool %s from cache LV %s.",
+		  display_lvname(cachepool_lv), display_lvname(lv));
+
+	if (!archive(lv->vg))
+		return_0;
+
+	if (!lv_cache_remove(lv))
+		return_0;
+
+	if (!vg_write(lv->vg) || !vg_commit(lv->vg))
+		return_0;
+
+	backup(lv->vg);
+
+	log_print_unless_silent("Logical volume %s is not cached and cache pool %s is unused.",
+				display_lvname(lv), display_lvname(cachepool_lv));
+
+	return 1;
+}
+
+static int _lvconvert_split_and_remove_cachepool(struct cmd_context *cmd,
+				   struct logical_volume *lv,
+				   struct logical_volume *cachepool_lv)
+{
+	struct lv_segment *seg;
+	struct logical_volume *remove_lv;
+
+	seg = first_seg(lv);
+
+	if (lv_is_partial(seg_lv(seg, 0))) {
+		log_warn("WARNING: Cache origin logical volume %s is missing.",
+			 display_lvname(seg_lv(seg, 0)));
+		remove_lv = lv; /* When origin is missing, drop everything */
+	} else
+		remove_lv = seg->pool_lv;
 
 	if (lv_is_partial(seg_lv(first_seg(seg->pool_lv), 0)))
 		log_warn("WARNING: Cache pool data logical volume %s is missing.",
@@ -2179,49 +2438,45 @@ deactivate_pmslv:
 	return 1;
 }
 
-/* Currently converts only to thin volume with external origin */
-static int _lvconvert_thin(struct cmd_context *cmd,
+static int _lvconvert_to_thin_with_external(struct cmd_context *cmd,
 			   struct logical_volume *lv,
-			   struct lvconvert_params *lp)
+			   struct logical_volume *thinpool_lv)
 {
-	struct logical_volume *torigin_lv, *pool_lv = lp->pool_data_lv;
 	struct volume_group *vg = lv->vg;
+	struct logical_volume *thin_lv;
+	const char *origin_name;
+
 	struct lvcreate_params lvc = {
 		.activate = CHANGE_AEY,
 		.alloc = ALLOC_INHERIT,
-		.lv_name = lp->origin_name,
 		.major = -1,
 		.minor = -1,
 		.suppress_zero_warn = 1, /* Suppress warning for this thin */
 		.permission = LVM_READ,
-		.pool_name = pool_lv->name,
+		.pool_name = thinpool_lv->name,
 		.pvh = &vg->pvs,
 		.read_ahead = DM_READ_AHEAD_AUTO,
 		.stripes = 1,
 		.virtual_extents = lv->le_count,
 	};
 
-	if (lv == pool_lv) {
+	if (lv == thinpool_lv) {
 		log_error("Can't use same LV %s for thin pool and thin volume.",
-			  display_lvname(pool_lv));
+			  display_lvname(thinpool_lv));
 		return 0;
 	}
 
-	if (lv_is_locked(lv) ||
-	    !lv_is_visible(lv) ||
-	    lv_is_cow(lv) ||
-	    lv_is_pool(lv) ||
-	    lv_is_pool_data(lv) ||
-	    lv_is_pool_metadata(lv)) {
-		log_error("Can't use%s%s %s %s as external origin.",
-			  lv_is_locked(lv) ? " locked" : "",
-			  lv_is_visible(lv) ? "" : " hidden",
-			  lvseg_name(first_seg(lv)),
-			  display_lvname(lv));
-		return 0;
-	}
+	if ((origin_name = arg_str_value(cmd, originname_ARG, NULL)))
+		if (!validate_restricted_lvname_param(cmd, &vg->name, &origin_name))
+			return_0;
+
+	/*
+	 * If NULL, an auto-generated 'lvol' name is used.
+	 * If set, the lv create code checks the name isn't used.
+	 */
+	lvc.lv_name = origin_name;
 
-	if (is_lockd_type(lv->vg->lock_type)) {
+	if (is_lockd_type(vg->lock_type)) {
 		/*
 		 * FIXME: external origins don't work in lockd VGs.
 		 * Prior to the lvconvert, there's a lock associated with
@@ -2233,13 +2488,13 @@ static int _lvconvert_thin(struct cmd_context *cmd,
 		 * lock for the new LV uuid used by the external LV.
 		 */
 		log_error("Can't use lock_type %s LV as external origin.",
-			  lv->vg->lock_type);
+			  vg->lock_type);
 		return 0;
 	}
 
 	dm_list_init(&lvc.tags);
 
-	if (!pool_supports_external_origin(first_seg(pool_lv), lv))
+	if (!pool_supports_external_origin(first_seg(thinpool_lv), lv))
 		return_0;
 
 	if (!(lvc.segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_THIN)))
@@ -2248,14 +2503,22 @@ static int _lvconvert_thin(struct cmd_context *cmd,
 	if (!archive(vg))
 		return_0;
 
-	/* New thin LV needs to be created (all messages sent to pool)
-	 * In this case thin volume is created READ-ONLY and
-	 * also warn about not zeroing is suppressed. */
-	if (!(torigin_lv = lv_create_single(vg, &lvc)))
+	/*
+	 * New thin LV needs to be created (all messages sent to pool) In this
+	 * case thin volume is created READ-ONLY and also warn about not
+	 * zeroing is suppressed.
+	 *
+	 * The new thin LV is created with the origin_name, or an autogenerated
+	 * 'lvol' name.  Then the names and ids are swapped between the thin LV
+	 * and the original/external LV.  So, the thin LV gets the name and id
+	 * of the original LV arg, and the original LV arg gets the origin_name
+	 * or the autogenerated name.
+	 */
+
+	if (!(thin_lv = lv_create_single(vg, &lvc)))
 		return_0;
 
-	/* Deactivate prepared Thin LV */
-	if (!deactivate_lv(cmd, torigin_lv)) {
+	if (!deactivate_lv(cmd, thin_lv)) {
 		log_error("Aborting. Unable to deactivate new LV. "
 			  "Manual intervention required.");
 		return 0;
@@ -2266,47 +2529,45 @@ static int _lvconvert_thin(struct cmd_context *cmd,
 	 * which could be easily removed by the user after i.e. power-off
 	 */
 
-	if (!swap_lv_identifiers(cmd, torigin_lv, lv)) {
+	if (!swap_lv_identifiers(cmd, thin_lv, lv)) {
 		stack;
 		goto revert_new_lv;
 	}
 
 	/* Preserve read-write status of original LV here */
-	torigin_lv->status |= (lv->status & LVM_WRITE);
+	thin_lv->status |= (lv->status & LVM_WRITE);
 
-	if (!attach_thin_external_origin(first_seg(torigin_lv), lv)) {
+	if (!attach_thin_external_origin(first_seg(thin_lv), lv)) {
 		stack;
 		goto revert_new_lv;
 	}
 
-	if (!lv_update_and_reload(torigin_lv)) {
+	if (!lv_update_and_reload(thin_lv)) {
 		stack;
 		goto deactivate_and_revert_new_lv;
 	}
 
-	log_print_unless_silent("Converted %s to thin volume with "
-				"external origin %s.",
-				display_lvname(torigin_lv),
-				display_lvname(lv));
+	log_print_unless_silent("Converted %s to thin volume with external origin %s.",
+				display_lvname(thin_lv), display_lvname(lv));
 
 	return 1;
 
 deactivate_and_revert_new_lv:
-	if (!swap_lv_identifiers(cmd, torigin_lv, lv))
+	if (!swap_lv_identifiers(cmd, thin_lv, lv))
 		stack;
 
-	if (!deactivate_lv(cmd, torigin_lv)) {
+	if (!deactivate_lv(cmd, thin_lv)) {
 		log_error("Unable to deactivate failed new LV. "
 			  "Manual intervention required.");
 		return 0;
 	}
 
-	if (!detach_thin_external_origin(first_seg(torigin_lv)))
+	if (!detach_thin_external_origin(first_seg(thin_lv)))
 		return_0;
 
 revert_new_lv:
 	/* FIXME Better to revert to backup of metadata? */
-	if (!lv_remove(torigin_lv) || !vg_write(vg) || !vg_commit(vg))
+	if (!lv_remove(thin_lv) || !vg_write(vg) || !vg_commit(vg))
 		log_error("Manual intervention may be required to remove "
 			  "abandoned LV(s) before retrying.");
 	else
@@ -2315,269 +2576,225 @@ revert_new_lv:
 	return 0;
 }
 
-static int _lvconvert_to_thin_with_external(struct cmd_context *cmd,
-			   struct logical_volume *lv,
-			   struct logical_volume *thinpool_lv)
+static int _lvconvert_swap_pool_metadata(struct cmd_context *cmd,
+					 struct logical_volume *lv,
+					 struct logical_volume *metadata_lv)
 {
 	struct volume_group *vg = lv->vg;
-	struct logical_volume *thin_lv;
-	const char *origin_name;
+	struct logical_volume *prev_metadata_lv;
+	struct lv_segment *seg;
+	struct lv_types *lvtype;
+	char meta_name[NAME_LEN];
+	const char *swap_name;
+	uint32_t chunk_size;
+	int is_thinpool;
+	int is_cachepool;
+	int lvt_enum;
 
-	struct lvcreate_params lvc = {
-		.activate = CHANGE_AEY,
-		.alloc = ALLOC_INHERIT,
-		.major = -1,
-		.minor = -1,
-		.suppress_zero_warn = 1, /* Suppress warning for this thin */
-		.permission = LVM_READ,
-		.pool_name = thinpool_lv->name,
-		.pvh = &vg->pvs,
-		.read_ahead = DM_READ_AHEAD_AUTO,
-		.stripes = 1,
-		.virtual_extents = lv->le_count,
-	};
+	is_thinpool = lv_is_thin_pool(lv);
+	is_cachepool = lv_is_cache_pool(lv);
+	lvt_enum = get_lvt_enum(metadata_lv);
+	lvtype = get_lv_type(lvt_enum);
 
-	if (lv == thinpool_lv) {
-		log_error("Can't use same LV %s for thin pool and thin volume.",
-			  display_lvname(thinpool_lv));
+	if (lvt_enum != striped_LVT && lvt_enum != linear_LVT && lvt_enum != raid_LVT) {
+		log_error("LV %s with type %s cannot be used as a metadata LV.",
+			  display_lvname(metadata_lv), lvtype ? lvtype->name : "unknown");
 		return 0;
 	}
 
-	if ((origin_name = arg_str_value(cmd, originname_ARG, NULL)))
-		if (!validate_restricted_lvname_param(cmd, &vg->name, &origin_name))
-			return_0;
+	if (!lv_is_visible(metadata_lv)) {
+		log_error("Can't convert internal LV %s.",
+			  display_lvname(metadata_lv));
+		return 0;
+	}
 
-	/*
-	 * If NULL, an auto-generated 'lvol' name is used.
-	 * If set, the lv create code checks the name isn't used.
-	 */
-	lvc.lv_name = origin_name;
+	if (lv_is_locked(metadata_lv)) {
+		log_error("Can't convert locked LV %s.",
+			  display_lvname(metadata_lv));
+		return 0;
+	}
 
-	if (is_lockd_type(vg->lock_type)) {
-		/*
-		 * FIXME: external origins don't work in lockd VGs.
-		 * Prior to the lvconvert, there's a lock associated with
-		 * the uuid of the external origin LV.  After the convert,
-		 * that uuid belongs to the new thin LV, and a new LV with
-		 * a new uuid exists as the non-thin, readonly external LV.
-		 * We'd need to remove the lock for the previous uuid
-		 * (the new thin LV will have no lock), and create a new
-		 * lock for the new LV uuid used by the external LV.
-		 */
-		log_error("Can't use lock_type %s LV as external origin.",
-			  vg->lock_type);
+	if (lv_is_origin(metadata_lv) ||
+	    lv_is_merging_origin(metadata_lv) ||
+	    lv_is_external_origin(metadata_lv) ||
+	    lv_is_virtual(metadata_lv)) {
+		log_error("Pool metadata LV %s is of an unsupported type.",
+			  display_lvname(metadata_lv));
 		return 0;
 	}
 
-	dm_list_init(&lvc.tags);
+	/* FIXME cache pool */
+	if (is_thinpool && pool_is_active(lv)) {
+		/* If any volume referencing pool active - abort here */
+		log_error("Cannot convert pool %s with active volumes.",
+			  display_lvname(lv));
+		return 0;
+	}
 
-	if (!pool_supports_external_origin(first_seg(thinpool_lv), lv))
-		return_0;
+	if ((dm_snprintf(meta_name, sizeof(meta_name), "%s%s", lv->name, is_cachepool ? "_cmeta" : "_tmeta") < 0)) {
+                log_error("Failed to create internal lv names, pool name is too long.");
+                return 0;
+        }
 
-	if (!(lvc.segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_THIN)))
-		return_0;
+	seg = first_seg(lv);
 
-	if (!archive(vg))
-		return_0;
+	/* Normally do NOT change chunk size when swapping */
 
-	/*
-	 * New thin LV needs to be created (all messages sent to pool) In this
-	 * case thin volume is created READ-ONLY and also warn about not
-	 * zeroing is suppressed.
-	 *
-	 * The new thin LV is created with the origin_name, or an autogenerated
-	 * 'lvol' name.  Then the names and ids are swapped between the thin LV
-	 * and the original/external LV.  So, the thin LV gets the name and id
-	 * of the original LV arg, and the original LV arg gets the origin_name
-	 * or the autogenerated name.
-	 */
+	if (arg_is_set(cmd, chunksize_ARG)) {
+		chunk_size = arg_uint_value(cmd, chunksize_ARG, 0);
 
-	if (!(thin_lv = lv_create_single(vg, &lvc)))
-		return_0;
+		if ((chunk_size != seg->chunk_size) && !dm_list_empty(&lv->segs_using_this_lv)) {
+			if (arg_count(cmd, force_ARG) == PROMPT) {
+				log_error("Chunk size can be only changed with --force. Conversion aborted.");
+				return 0;
+			}
 
-	if (!deactivate_lv(cmd, thin_lv)) {
-		log_error("Aborting. Unable to deactivate new LV. "
-			  "Manual intervention required.");
-		return 0;
-	}
+			if (!validate_pool_chunk_size(cmd, seg->segtype, chunk_size))
+				return_0;
 
-	/*
-	 * Crashing till this point will leave plain thin volume
-	 * which could be easily removed by the user after i.e. power-off
-	 */
+			log_warn("WARNING: Changing chunk size %s to %s for %s pool volume.",
+				 display_size(cmd, seg->chunk_size),
+				 display_size(cmd, chunk_size),
+				 display_lvname(lv));
 
-	if (!swap_lv_identifiers(cmd, thin_lv, lv)) {
-		stack;
-		goto revert_new_lv;
-	}
+			/* Ok, user has likely some serious reason for this */
+			if (!arg_count(cmd, yes_ARG) &&
+			    yes_no_prompt("Do you really want to change chunk size for %s pool volume? [y/n]: ",
+					  display_lvname(lv)) == 'n') {
+				log_error("Conversion aborted.");
+				return 0;
+			}
+		}
 
-	/* Preserve read-write status of original LV here */
-	thin_lv->status |= (lv->status & LVM_WRITE);
+		seg->chunk_size = chunk_size;
+	}
 
-	if (!attach_thin_external_origin(first_seg(thin_lv), lv)) {
-		stack;
-		goto revert_new_lv;
+	if (!arg_count(cmd, yes_ARG) &&
+	    yes_no_prompt("Do you want to swap metadata of %s pool with metadata volume %s? [y/n]: ",
+			  display_lvname(lv),
+			  display_lvname(metadata_lv)) == 'n') {
+		log_error("Conversion aborted.");
+		return 0;
 	}
 
-	if (!lv_update_and_reload(thin_lv)) {
-		stack;
-		goto deactivate_and_revert_new_lv;
+	if (!deactivate_lv(cmd, metadata_lv)) {
+		log_error("Aborting. Failed to deactivate %s.",
+			  display_lvname(metadata_lv));
+		return 0;
 	}
 
-	log_print_unless_silent("Converted %s to thin volume with external origin %s.",
-				display_lvname(thin_lv), display_lvname(lv));
+	if (!archive(vg))
+		return_0;
 
-	return 1;
+	/* Swap names between old and new metadata LV */
 
-deactivate_and_revert_new_lv:
-	if (!swap_lv_identifiers(cmd, thin_lv, lv))
-		stack;
+	if (!detach_pool_metadata_lv(seg, &prev_metadata_lv))
+		return_0;
 
-	if (!deactivate_lv(cmd, thin_lv)) {
-		log_error("Unable to deactivate failed new LV. "
-			  "Manual intervention required.");
-		return 0;
-	}
+	swap_name = metadata_lv->name;
 
-	if (!detach_thin_external_origin(first_seg(thin_lv)))
+	if (!lv_rename_update(cmd, metadata_lv, "pvmove_tmeta", 0))
 		return_0;
 
-revert_new_lv:
-	/* FIXME Better to revert to backup of metadata? */
-	if (!lv_remove(thin_lv) || !vg_write(vg) || !vg_commit(vg))
-		log_error("Manual intervention may be required to remove "
-			  "abandoned LV(s) before retrying.");
-	else
-		backup(vg);
+	/* Give the previous metadata LV the name of the LV replacing it. */
 
-	return 0;
-}
+	if (!lv_rename_update(cmd, prev_metadata_lv, swap_name, 0))
+		return_0;
 
-static int _lvconvert_update_pool_params(struct logical_volume *pool_lv,
-					 struct lvconvert_params *lp)
-{
-	if (lp->pool_metadata_size &&
-	    !(lp->pool_metadata_extents =
-	      extents_from_size(pool_lv->vg->cmd, lp->pool_metadata_size, pool_lv->vg->extent_size)))
+	/* Rename deactivated metadata LV to have _tmeta suffix */
+
+	if (!lv_rename_update(cmd, metadata_lv, meta_name, 0))
+		return_0;
+
+	if (!attach_pool_metadata_lv(seg, metadata_lv))
+		return_0;
+
+	if (!vg_write(vg) || !vg_commit(vg))
 		return_0;
 
-	return update_pool_params(lp->segtype,
-				  pool_lv->vg,
-				  lp->target_attr,
-				  lp->passed_args,
-				  pool_lv->le_count,
-				  &lp->pool_metadata_extents,
-				  &lp->thin_chunk_size_calc_policy,
-				  &lp->chunk_size,
-				  &lp->discards,
-				  &lp->zero);
+	backup(vg);
+	return 1;
 }
 
 /*
- * Converts a data lv and a metadata lv into a thin or cache pool lv.
- *
- * Thin lvconvert version which
- *  rename metadata
- *  convert/layers thinpool over data
- *  attach metadata
- *
- * pool_lv might or might not already be a pool.
+ * Create a new pool LV, using the lv arg as the data sub LV.
+ * The metadata sub LV is either a new LV created here, or an
+ * existing LV specified by --poolmetadata.
  */
-static int _lvconvert_pool(struct cmd_context *cmd,
-			   struct logical_volume *pool_lv,
-			   struct lvconvert_params *lp)
+
+static int _lvconvert_to_pool(struct cmd_context *cmd,
+			      struct logical_volume *lv,
+			      int to_thinpool,
+			      int to_cachepool,
+			      struct dm_list *use_pvh)
 {
-	int r = 0;
-	const char *old_name;
+	struct volume_group *vg = lv->vg;
+	struct logical_volume *metadata_lv = NULL;  /* existing or created */
+	struct logical_volume *data_lv;             /* lv arg renamed */
+	struct logical_volume *pool_lv;             /* new lv created here */
+	const char *pool_metadata_name;             /* user-specified lv name */
+	const char *pool_name;                      /* name of original lv arg */
+	char meta_name[NAME_LEN];                   /* generated sub lv name */
+	char data_name[NAME_LEN];                   /* generated sub lv name */
+	struct segment_type *pool_segtype;          /* thinpool or cachepool */
 	struct lv_segment *seg;
-	struct volume_group *vg = pool_lv->vg;
-	struct logical_volume *data_lv;
-	struct logical_volume *metadata_lv = NULL;
-	struct logical_volume *pool_metadata_lv;
+	unsigned int target_attr = ~0;
+	unsigned int passed_args = 0;
+	unsigned int activate_pool;
+	unsigned int zero_metadata;
+	uint64_t meta_size;
+	uint32_t meta_extents;
+	uint32_t chunk_size;
+	int chunk_calc;
+	int r = 0;
+
+	/* for handling lvmlockd cases */
 	char *lockd_data_args = NULL;
 	char *lockd_meta_args = NULL;
 	char *lockd_data_name = NULL;
 	char *lockd_meta_name = NULL;
 	struct id lockd_data_id;
 	struct id lockd_meta_id;
-	char metadata_name[NAME_LEN], data_name[NAME_LEN];
-	int zero_metadata = 1;
-	int activate_pool;
-
-	if (lp->pool_data_name) {
-		if ((lp->thin || lp->cache) &&
-		    !strcmp(lp->pool_data_name, pool_lv->name)) {
-			log_error("Converted volume %s and pool volume must differ.",
-				  display_lvname(pool_lv));
-			return 0;
-		}
-		if (!(pool_lv = find_lv(vg, lp->pool_data_name))) {
-			log_error("Unknown pool data LV %s.", lp->pool_data_name);
-			return 0;
-		}
-	}
-
-	/* An existing LV needs to have its lock freed once it becomes a data LV. */
-	if (is_lockd_type(vg->lock_type) && !lv_is_pool(pool_lv) && pool_lv->lock_args) {
-		lockd_data_args = dm_pool_strdup(cmd->mem, pool_lv->lock_args);
-		lockd_data_name = dm_pool_strdup(cmd->mem, pool_lv->name);
-		memcpy(&lockd_data_id, &pool_lv->lvid.id[1], sizeof(struct id));
-	}
 
-	if (!lv_is_visible(pool_lv)) {
-		log_error("Can't convert internal LV %s.", display_lvname(pool_lv));
-		return 0;
-	}
 
-	if (lv_is_locked(pool_lv)) {
-		log_error("Can't convert locked LV %s.", display_lvname(pool_lv));
+	if (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) {
+		log_error(INTERNAL_ERROR "LV %s is already a pool.", display_lvname(lv));
 		return 0;
 	}
 
-	if (lv_is_thin_pool(pool_lv) && (segtype_is_cache_pool(lp->segtype) || lp->cache)) {
-		log_error("Can't convert thin pool LV %s.", display_lvname(pool_lv));
-		return 0;
-	}
+	pool_segtype = to_cachepool ? get_segtype_from_string(cmd, SEG_TYPE_NAME_CACHE_POOL) :
+				      get_segtype_from_string(cmd, SEG_TYPE_NAME_THIN_POOL);
 
-	if (lv_is_cache(pool_lv) && !segtype_is_thin_pool(lp->segtype)) {
-		log_error("Cached LV %s could be only converted into a thin pool volume.",
-			  display_lvname(pool_lv));
+	if (!pool_segtype->ops->target_present(cmd, NULL, &target_attr)) {
+		log_error("%s: Required device-mapper target(s) not detected in your kernel.", pool_segtype->name);
 		return 0;
 	}
 
-	if (lv_is_cache_pool(pool_lv) && (segtype_is_thin_pool(lp->segtype) || lp->thin)) {
-		log_error("Cannot convert cache pool %s as pool data volume.",
-			  display_lvname(pool_lv));
-		return 0;
-	}
+	/* Allow to have only thinpool active and restore it's active state. */
+	activate_pool = to_thinpool && lv_is_active(lv);
 
-	if (lv_is_mirror(pool_lv)) {
-		log_error("Mirror logical volumes cannot be used as pools.");
-		log_print_unless_silent("Try \"%s\" segment type instead.", SEG_TYPE_NAME_RAID1);
-		return 0;
+	/* Wipe metadata_lv by default, but allow skipping this for cache pools. */
+	zero_metadata = to_cachepool ? arg_int_value(cmd, zero_ARG, 1) : 1;
+
+	/* An existing LV needs to have its lock freed once it becomes a data LV. */
+	if (is_lockd_type(vg->lock_type) && lv->lock_args) {
+		lockd_data_args = dm_pool_strdup(cmd->mem, lv->lock_args);
+		lockd_data_name = dm_pool_strdup(cmd->mem, lv->name);
+		memcpy(&lockd_data_id, &lv->lvid.id[1], sizeof(struct id));
 	}
 
 	/*
-	 * Only linear, striped and raid supported.
-	 * FIXME Tidy up all these type restrictions.
+	 * If an existing LV is to be used as the metadata LV,
+	 * verify that it's in a usable state.  These checks are
+	 * not done by command def rules because this LV is not
+	 * processed by process_each_lv.
 	 */
-	if (!lv_is_pool(pool_lv) &&
-	    (lv_is_thin_type(pool_lv) ||
-	     lv_is_cow(pool_lv) || lv_is_merging_cow(pool_lv) ||
-	     lv_is_origin(pool_lv) ||lv_is_merging_origin(pool_lv) ||
-	     lv_is_external_origin(pool_lv) ||
-	     lv_is_virtual(pool_lv))) {
-		log_error("Pool data LV %s is of an unsupported type.", display_lvname(pool_lv));
-		return 0;
-	}
 
-	if (lp->pool_metadata_name) {
-		if (!(lp->pool_metadata_lv = find_lv(vg, lp->pool_metadata_name))) {
-			log_error("Unknown pool metadata LV %s.", lp->pool_metadata_name);
+	if ((pool_metadata_name = arg_str_value(cmd, poolmetadata_ARG, NULL))) {
+		if (!(metadata_lv = find_lv(vg, pool_metadata_name))) {
+			log_error("Unknown pool metadata LV %s.", pool_metadata_name);
 			return 0;
 		}
-		lp->pool_metadata_extents = lp->pool_metadata_lv->le_count;
-		metadata_lv = lp->pool_metadata_lv;
 
 		/* An existing LV needs to have its lock freed once it becomes a meta LV. */
 		if (is_lockd_type(vg->lock_type) && metadata_lv->lock_args) {
@@ -2586,7 +2803,7 @@ static int _lvconvert_pool(struct cmd_context *cmd,
 			memcpy(&lockd_meta_id, &metadata_lv->lvid.id[1], sizeof(struct id));
 		}
 
-		if (metadata_lv == pool_lv) {
+		if (metadata_lv == lv) {
 			log_error("Can't use same LV for pool data and metadata LV %s.",
 				  display_lvname(metadata_lv));
 			return 0;
@@ -2621,199 +2838,152 @@ static int _lvconvert_pool(struct cmd_context *cmd,
 				  display_lvname(metadata_lv));
 			return 0;
 		}
-
-		if (!lv_is_pool(pool_lv)) {
-			if (!_lvconvert_update_pool_params(pool_lv, lp))
-				return_0;
-
-			if (lp->pool_metadata_extents > metadata_lv->le_count) {
-				log_error("Logical volume %s is too small for metadata.",
-					  display_lvname(metadata_lv));
-				return 0;
-			}
-		}
 	}
 
-	if (lv_is_pool(pool_lv)) {
-		lp->pool_data_lv = pool_lv;
+	/*
+	 * Determine the size of the metadata LV and the chunk size.  When an
+	 * existing LV is to be used for metadata, this introduces some
+	 * constraints/defaults.  When chunk_size=0 and/or meta_extents=0 are
+	 * passed to the "update params" function, defaults are calculated and
+	 * returned.
+	 */
 
-		if (!metadata_lv) {
-			if (arg_from_list_is_set(cmd, "is invalid with existing pool",
-						 discards_ARG,
-						 poolmetadatasize_ARG, -1))
-				return_0;
+	if (arg_is_set(cmd, chunksize_ARG)) {
+		passed_args |= PASS_ARG_CHUNK_SIZE;
+		chunk_size = arg_uint_value(cmd, chunksize_ARG, 0);
+		if (!validate_pool_chunk_size(cmd, pool_segtype, chunk_size))
+			return_0;
+	} else {
+		/* A default will be chosen by the "update" function. */
+		chunk_size = 0;
+	}
 
-			if (lp->thin &&
-			    arg_from_list_is_set(cmd, "is invalid with existing thin pool",
-						 chunksize_ARG, zero_ARG, -1))
-				return_0;
+	if (arg_is_set(cmd, poolmetadatasize_ARG)) {
+		meta_size = arg_uint64_value(cmd, poolmetadatasize_ARG, UINT64_C(0));
+		meta_extents = extents_from_size(cmd, meta_size, vg->extent_size);
+		passed_args |= PASS_ARG_POOL_METADATA_SIZE;
+	} else if (metadata_lv) {
+		meta_extents = metadata_lv->le_count;
+		passed_args |= PASS_ARG_POOL_METADATA_SIZE;
+	} else {
+		/* A default will be chosen by the "update" function. */
+		meta_extents = 0;
+	}
 
-			if (lp->cache) {
-				if (!lp->chunk_size)
-					lp->chunk_size = first_seg(pool_lv)->chunk_size;
-
-				if (!validate_lv_cache_chunk_size(pool_lv, lp->chunk_size))
-					return_0;
-
-				/* Check is user requested zeroing logic via [-Z y|n] */
-				if (!arg_is_set(cmd, zero_ARG)) {
-					/* Note: requires rather deep know-how to skip zeroing */
-					if (!lp->yes &&
-					    yes_no_prompt("Do you want wipe existing metadata of "
-							  "cache pool volume %s? [y/n]: ",
-							  display_lvname(pool_lv)) == 'n') {
-						log_error("Conversion aborted.");
-						log_error("To preserve cache metadata add option \"--zero n\".");
-						log_warn("WARNING: Reusing mismatched cache pool metadata "
-							 "MAY DESTROY YOUR DATA!");
-						return 0;
-					}
-					/* Wiping confirmed, go ahead */
-					if (!wipe_cache_pool(pool_lv))
-						return_0;
-				} else if (arg_int_value(cmd, zero_ARG, 0)) {
-					if (!wipe_cache_pool(pool_lv)) /* Wipe according to -Z y|n */
-						return_0;
-				} else
-					log_warn("WARNING: Reusing cache pool metadata %s "
-						 "for volume caching.", display_lvname(pool_lv));
-			}
+	/* Tell the "update" function to ignore these, they are handled below. */
+	passed_args |= PASS_ARG_DISCARDS | PASS_ARG_ZERO;
 
-			if (lp->thin || lp->cache)
-				/* already pool, can continue converting volume */
-				return 1;
+	/*
+	 * Validate and/or choose defaults for meta_extents and chunk_size,
+	 * this involves some complicated calculations.
+	 */
 
-			log_error("LV %s is already pool.", display_lvname(pool_lv));
-			return 0;
-		}
+	if (to_cachepool) {
+		if (!update_cache_pool_params(pool_segtype, vg, target_attr,
+					      passed_args, lv->le_count,
+					      &meta_extents,
+					      &chunk_calc,
+					      &chunk_size))
+			return_0;
+	} else {
+		if (!update_thin_pool_params(pool_segtype, vg, target_attr,
+					     passed_args, lv->le_count,
+					     &meta_extents,
+					     &chunk_calc,
+					     &chunk_size,
+					     NULL, NULL))
+			return_0;
+	}
 
-		if (lp->thin || lp->cache) {
-			log_error("--%s and pool metadata swap is not supported.",
-				  lp->thin ? "thin" : "cache");
-			return 0;
-		}
+	if ((uint64_t)chunk_size > ((uint64_t)lv->le_count * vg->extent_size)) {
+		log_error("Pool data LV %s is too small (%s) for specified chunk size (%s).",
+			  display_lvname(lv),
+			  display_size(cmd, (uint64_t)lv->le_count * vg->extent_size),
+			  display_size(cmd, chunk_size));
+		return 0;
+	}
 
-		/* FIXME cache pool */
-		if (lv_is_thin_pool(pool_lv) && pool_is_active(pool_lv)) {
-			/* If any volume referencing pool active - abort here */
-			log_error("Cannot convert pool %s with active volumes.",
-				  display_lvname(pool_lv));
-			return 0;
-		}
+	if (metadata_lv && (meta_extents > metadata_lv->le_count)) {
+		log_error("Pool metadata LV %s is too small (%u extents) for required metadata (%u extents).",
+			  display_lvname(metadata_lv), metadata_lv->le_count, meta_extents);
+		return 0;
+	}
 
-		lp->passed_args |= PASS_ARG_CHUNK_SIZE | PASS_ARG_DISCARDS | PASS_ARG_ZERO;
-		seg = first_seg(pool_lv);
+	log_verbose("Pool metadata extents %u chunk_size %u", meta_extents, chunk_size);
 
-		/* Normally do NOT change chunk size when swapping */
-		if (arg_is_set(cmd, chunksize_ARG) &&
-		    (lp->chunk_size != seg->chunk_size) &&
-		    !dm_list_empty(&pool_lv->segs_using_this_lv)) {
-			if (lp->force == PROMPT) {
-				log_error("Chunk size can be only changed with --force. Conversion aborted.");
-				return 0;
-			}
-			log_warn("WARNING: Changing chunk size %s to "
-				 "%s for %s pool volume.",
-				 display_size(cmd, seg->chunk_size),
-				 display_size(cmd, lp->chunk_size),
-				 display_lvname(pool_lv));
-			/* Ok, user has likely some serious reason for this */
-			if (!lp->yes &&
-			    yes_no_prompt("Do you really want to change chunk size "
-					  "for %s pool volume? [y/n]: ",
-					  display_lvname(pool_lv)) == 'n') {
-				log_error("Conversion aborted.");
-				return 0;
-			}
-		} else
-			lp->chunk_size = seg->chunk_size;
 
-		if (!_lvconvert_update_pool_params(pool_lv, lp))
-			return_0;
+	/*
+	 * Verify that user wants to use these LVs.
+	 */
 
-		if (metadata_lv->le_count < lp->pool_metadata_extents)
-			log_print_unless_silent("Continuing with swap...");
+	log_warn("WARNING: Converting logical volume %s%s%s to %s pool's data%s %s metadata wiping.",
+		 display_lvname(lv),
+		 metadata_lv ? " and " : "",
+		 metadata_lv ? display_lvname(metadata_lv) : "",
+		 to_cachepool ? "cache" : "thin",
+		 metadata_lv ? " and metadata volumes" : " volume",
+		 zero_metadata ? "with" : "WITHOUT");
 
-		if (!arg_is_set(cmd, discards_ARG))
-			lp->discards = seg->discards;
-		if (!arg_is_set(cmd, zero_ARG))
-			lp->zero = seg->zero_new_blocks;
+	if (zero_metadata)
+		log_warn("THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)");
+	else if (to_cachepool)
+		log_warn("WARNING: Using mismatched cache pool metadata MAY DESTROY YOUR DATA!");
 
-		if (!lp->yes &&
-		    yes_no_prompt("Do you want to swap metadata of %s "
-				  "pool with metadata volume %s? [y/n]: ",
-				  display_lvname(pool_lv),
-				  display_lvname(metadata_lv)) == 'n') {
-			log_error("Conversion aborted.");
-			return 0;
-		}
-	} else {
-		/* Only cache pool conversion may suppress metadata zeroing
-		 * TODO: Maybe similar support could be useful for thin-pool, but --zero
-		 * is already overloade and we would also need to possibly match transaction Id. */
-		if (segtype_is_cache_pool(lp->segtype) && metadata_lv)
-			/* Check is user requested zeroing logic via [-Z y|n] (default is yes) */
-			zero_metadata = arg_int_value(cmd, zero_ARG, 1);
-
-		log_warn("WARNING: Converting logical volume %s%s%s to %s pool's data%s %s metadata wiping.",
-			 display_lvname(pool_lv),
-			 metadata_lv ? " and " : "",
-			 metadata_lv ? display_lvname(metadata_lv) : "",
-			 segtype_is_cache_pool(lp->segtype) ? "cache" : "thin",
-			 metadata_lv ? " and metadata volumes" : " volume",
-			 zero_metadata ? "with" : "WITHOUT");
-
-		if (zero_metadata)
-			log_warn("THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)");
-		else /* ATM supported only for cache pools */
-			log_warn("WARNING: Using mismatched cache pool metadata "
-				 "MAY DESTROY YOUR DATA!");
-
-		if (!lp->yes &&
-		    yes_no_prompt("Do you really want to convert %s%s%s? [y/n]: ",
-				  display_lvname(pool_lv),
-				  metadata_lv ? " and " : "",
-				  metadata_lv ? display_lvname(metadata_lv) : "") == 'n') {
-			log_error("Conversion aborted.");
-			return 0;
-		}
+	if (!arg_count(cmd, yes_ARG) &&
+	    yes_no_prompt("Do you really want to convert %s%s%s? [y/n]: ",
+			  display_lvname(lv),
+			  metadata_lv ? " and " : "",
+			  metadata_lv ? display_lvname(metadata_lv) : "") == 'n') {
+		log_error("Conversion aborted.");
+		return 0;
 	}
 
-	if (segtype_is_cache_pool(lp->segtype))
-		activate_pool = 0; /* Cannot activate cache pool */
-	else
-		/* Allow to have only thinpool active and restore it's active state */
-		activate_pool = lv_is_active(pool_lv);
-
-	if ((dm_snprintf(metadata_name, sizeof(metadata_name), "%s%s",
-			 pool_lv->name,
-			 (segtype_is_cache_pool(lp->segtype)) ?
-			  "_cmeta" : "_tmeta") < 0) ||
-	    (dm_snprintf(data_name, sizeof(data_name), "%s%s",
-			 pool_lv->name,
-			 (segtype_is_cache_pool(lp->segtype)) ?
-			 "_cdata" : "_tdata") < 0)) {
-		log_error("Failed to create internal lv names, "
-			  "pool name is too long.");
+	/*
+	 * The internal LV names for pool data/meta LVs.
+	 */
+
+	if ((dm_snprintf(meta_name, sizeof(meta_name), "%s%s", lv->name, to_cachepool ? "_cmeta" : "_tmeta") < 0) ||
+	    (dm_snprintf(data_name, sizeof(data_name), "%s%s", lv->name, to_cachepool ? "_cdata" : "_tdata") < 0)) {
+		log_error("Failed to create internal lv names, pool name is too long.");
 		return 0;
 	}
 
+	/*
+	 * If a new metadata LV needs to be created, collect the settings for
+	 * the new LV and create it.
+	 *
+	 * If an existing LV is used for metadata, deactivate/activate/wipe it.
+	 */
+
 	if (!metadata_lv) {
-		if (!_lvconvert_update_pool_params(pool_lv, lp))
-			return_0;
+		uint32_t meta_stripes;
+		uint32_t meta_stripe_size;
+		uint32_t meta_readahead;
+		alloc_policy_t meta_alloc;
+		unsigned meta_stripes_supplied;
+		unsigned meta_stripe_size_supplied;
 
 		if (!get_stripe_params(cmd, get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED),
-				       &lp->stripes, &lp->stripe_size, &lp->stripes_supplied, &lp->stripe_size_supplied))
+				       &meta_stripes,
+				       &meta_stripe_size,
+				       &meta_stripes_supplied,
+				       &meta_stripe_size_supplied))
 			return_0;
 
+		meta_readahead = arg_uint_value(cmd, readahead_ARG, cmd->default_settings.read_ahead);
+		meta_alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT);
+
 		if (!archive(vg))
 			return_0;
 
-		if (!(metadata_lv = alloc_pool_metadata(pool_lv, metadata_name,
-							lp->read_ahead, lp->stripes,
-							lp->stripe_size,
-							lp->pool_metadata_extents,
-							lp->alloc, lp->pvh)))
+		if (!(metadata_lv = alloc_pool_metadata(lv,
+							meta_name,
+							meta_readahead,
+							meta_stripes,
+							meta_stripe_size,
+							meta_extents,
+							meta_alloc,
+							use_pvh)))
 			return_0;
 	} else {
 		if (!deactivate_lv(cmd, metadata_lv)) {
@@ -2825,21 +2995,6 @@ static int _lvconvert_pool(struct cmd_context *cmd,
 		if (!archive(vg))
 			return_0;
 
-		/* Swap normal LV with pool's metadata LV ? */
-		if (lv_is_pool(pool_lv)) {
-			/* Swap names between old and new metadata LV */
-			seg = first_seg(pool_lv);
-			if (!detach_pool_metadata_lv(seg, &pool_metadata_lv))
-				return_0;
-			old_name = metadata_lv->name;
-			if (!lv_rename_update(cmd, metadata_lv, "pvmove_tmeta", 0))
-				return_0;
-			if (!lv_rename_update(cmd, pool_metadata_lv, old_name, 0))
-				return_0;
-
-			goto mda_write;
-		}
-
 		if (zero_metadata) {
 			metadata_lv->status |= LV_TEMPORARY;
 			if (!activate_lv_local(cmd, metadata_lv)) {
@@ -2854,41 +3009,66 @@ static int _lvconvert_pool(struct cmd_context *cmd,
 		}
 	}
 
-	/* We are changing target type, so deactivate first */
+	/*
+	 * Deactivate the data LV and metadata LV.
+	 * We are changing target type, so deactivate first.
+	 */
+
 	if (!deactivate_lv(cmd, metadata_lv)) {
 		log_error("Aborting. Failed to deactivate metadata lv. "
 			  "Manual intervention required.");
 		return 0;
 	}
 
-	if (!deactivate_lv(cmd, pool_lv)) {
+	if (!deactivate_lv(cmd, lv)) {
 		log_error("Aborting. Failed to deactivate logical volume %s.",
-			  display_lvname(pool_lv));
+			  display_lvname(lv));
 		return 0;
 	}
 
-	data_lv = pool_lv;
-	old_name = data_lv->name; /* Use for pool name */
 	/*
+	 * When the LV referenced by the original function arg "lv"
+	 * is renamed, it is then referenced as "data_lv".
+	 *
+	 * pool_name    pool name taken from lv arg
+	 * data_name    sub lv name, generated
+	 * meta_name    sub lv name, generated
+	 *
+	 * pool_lv      new lv for pool object, created here
+	 * data_lv      sub lv, was lv arg, now renamed
+	 * metadata_lv  sub lv, existing or created here
+	 */
+
+	data_lv = lv;
+	pool_name = lv->name; /* Use original LV name for pool name */
+
+	/*
+	 * Rename the original LV arg to the internal data LV naming scheme.
+	 *
 	 * Since we wish to have underlaying devs to match _[ct]data
 	 * rename data LV to match pool LV subtree first,
 	 * also checks for visible LV.
+	 *
+	 * FIXME: any more types prohibited here?
 	 */
-	/* FIXME: any more types prohibited here? */
+
 	if (!lv_rename_update(cmd, data_lv, data_name, 0))
 		return_0;
 
-	if (!(pool_lv = lv_create_empty(old_name, NULL,
-					((segtype_is_cache_pool(lp->segtype)) ?
-					 CACHE_POOL : THIN_POOL) |
-					VISIBLE_LV | LVM_READ | LVM_WRITE,
+	/*
+	 * Create LV structures for the new pool LV object,
+	 * and connect it to the data/meta LVs.
+	 */
+
+	if (!(pool_lv = lv_create_empty(pool_name, NULL,
+					(to_cachepool ? CACHE_POOL : THIN_POOL) | VISIBLE_LV | LVM_READ | LVM_WRITE,
 					ALLOC_INHERIT, vg))) {
 		log_error("Creation of pool LV failed.");
 		return 0;
 	}
 
 	/* Allocate a new pool segment */
-	if (!(seg = alloc_lv_segment(lp->segtype, pool_lv, 0, data_lv->le_count,
+	if (!(seg = alloc_lv_segment(pool_segtype, pool_lv, 0, data_lv->le_count,
 				     pool_lv->status, 0, NULL, 1,
 				     data_lv->le_count, 0, 0, 0, NULL)))
 		return_0;
@@ -2907,7 +3087,7 @@ static int _lvconvert_pool(struct cmd_context *cmd,
 	 * data and meta LVs (they are unlocked and deleted below.)
 	 */
 	if (is_lockd_type(vg->lock_type)) {
-		if (segtype_is_cache_pool(lp->segtype)) {
+		if (to_cachepool) {
 			data_lv->lock_args = NULL;
 			metadata_lv->lock_args = NULL;
 		} else {
@@ -2919,1763 +3099,182 @@ static int _lvconvert_pool(struct cmd_context *cmd,
 			else if (!strcmp(vg->lock_type, "dlm"))
 				pool_lv->lock_args = "dlm";
 			/* The lock_args will be set in vg_write(). */
-		}
-	}
-
-	/* FIXME: revert renamed LVs in fail path? */
-	/* FIXME: any common code with metadata/thin_manip.c  extend_pool() ? */
-
-	seg->transaction_id = 0;
-
-mda_write:
-	seg->chunk_size = lp->chunk_size;
-	seg->discards = lp->discards;
-	seg->zero_new_blocks = lp->zero ? 1 : 0;
-
-	if (lp->cache_mode &&
-	    !cache_set_cache_mode(seg, lp->cache_mode))
-		return_0;
-
-	if ((lp->policy_name || lp->policy_settings) &&
-	    !cache_set_policy(seg, lp->policy_name, lp->policy_settings))
-		return_0;
-
-	/* Rename deactivated metadata LV to have _tmeta suffix */
-	/* Implicit checks if metadata_lv is visible */
-	if (lp->pool_metadata_name &&
-	    !lv_rename_update(cmd, metadata_lv, metadata_name, 0))
-		return_0;
-
-	if (!attach_pool_metadata_lv(seg, metadata_lv))
-		return_0;
-
-	if (!handle_pool_metadata_spare(vg, metadata_lv->le_count,
-					lp->pvh, lp->poolmetadataspare))
-		return_0;
-
-	if (!vg_write(vg) || !vg_commit(vg))
-		return_0;
-
-	if (seg->zero_new_blocks &&
-	    seg->chunk_size  >= DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE * 2)
-		log_warn("WARNING: Pool zeroing and large %s chunk size slows down "
-			 "provisioning.", display_size(cmd, seg->chunk_size));
-
-	if (activate_pool && !lockd_lv(cmd, pool_lv, "ex", LDLV_PERSISTENT)) {
-		log_error("Failed to lock pool LV %s.", display_lvname(pool_lv));
-		goto out;
-	}
-
-	if (activate_pool &&
-	    !activate_lv_excl(cmd, pool_lv)) {
-		log_error("Failed to activate pool logical volume %s.",
-			  display_lvname(pool_lv));
-		/* Deactivate subvolumes */
-		if (!deactivate_lv(cmd, seg_lv(seg, 0)))
-			log_error("Failed to deactivate pool data logical volume %s.",
-				  display_lvname(seg_lv(seg, 0)));
-		if (!deactivate_lv(cmd, seg->metadata_lv))
-			log_error("Failed to deactivate pool metadata logical volume %s.",
-				  display_lvname(seg->metadata_lv));
-		goto out;
-	}
-
-	r = 1;
-	lp->pool_data_lv = pool_lv;
-
-out:
-	backup(vg);
-
-	if (r)
-		log_print_unless_silent("Converted %s to %s pool.",
-					display_lvname(pool_lv),
-					(segtype_is_cache_pool(lp->segtype)) ?
-					"cache" : "thin");
-
-	/*
-	 * Unlock and free the locks from existing LVs that became pool data
-	 * and meta LVs.
-	 */
-	if (lockd_data_name) {
-		if (!lockd_lv_name(cmd, vg, lockd_data_name, &lockd_data_id, lockd_data_args, "un", LDLV_PERSISTENT))
-			log_error("Failed to unlock pool data LV %s/%s", vg->name, lockd_data_name);
-		lockd_free_lv(cmd, vg, lockd_data_name, &lockd_data_id, lockd_data_args);
-	}
-
-	if (lockd_meta_name) {
-		if (!lockd_lv_name(cmd, vg, lockd_meta_name, &lockd_meta_id, lockd_meta_args, "un", LDLV_PERSISTENT))
-			log_error("Failed to unlock pool metadata LV %s/%s", vg->name, lockd_meta_name);
-		lockd_free_lv(cmd, vg, lockd_meta_name, &lockd_meta_id, lockd_meta_args);
-	}
-
-	return r;
-#if 0
-revert_new_lv:
-	/* TBD */
-	if (!lp->pool_metadata_lv_name) {
-		if (!deactivate_lv(cmd, metadata_lv)) {
-			log_error("Failed to deactivate metadata lv.");
-			return 0;
-		}
-		if (!lv_remove(metadata_lv) || !vg_write(vg) || !vg_commit(vg))
-			log_error("Manual intervention may be required to remove "
-				  "abandoned LV(s) before retrying.");
-		else
-			backup(vg);
-	}
-
-	return 0;
-#endif
-}
-
-static int _lvconvert_swap_pool_metadata(struct cmd_context *cmd,
-					 struct logical_volume *lv,
-					 struct logical_volume *metadata_lv)
-{
-	struct volume_group *vg = lv->vg;
-	struct logical_volume *prev_metadata_lv;
-	struct lv_segment *seg;
-	struct lv_types *lvtype;
-	char meta_name[NAME_LEN];
-	const char *swap_name;
-	uint32_t chunk_size;
-	int is_thinpool;
-	int is_cachepool;
-	int lvt_enum;
-
-	is_thinpool = lv_is_thin_pool(lv);
-	is_cachepool = lv_is_cache_pool(lv);
-	lvt_enum = get_lvt_enum(metadata_lv);
-	lvtype = get_lv_type(lvt_enum);
-
-	if (lvt_enum != striped_LVT && lvt_enum != linear_LVT && lvt_enum != raid_LVT) {
-		log_error("LV %s with type %s cannot be used as a metadata LV.",
-			  display_lvname(metadata_lv), lvtype ? lvtype->name : "unknown");
-		return 0;
-	}
-
-	if (!lv_is_visible(metadata_lv)) {
-		log_error("Can't convert internal LV %s.",
-			  display_lvname(metadata_lv));
-		return 0;
-	}
-
-	if (lv_is_locked(metadata_lv)) {
-		log_error("Can't convert locked LV %s.",
-			  display_lvname(metadata_lv));
-		return 0;
-	}
-
-	if (lv_is_origin(metadata_lv) ||
-	    lv_is_merging_origin(metadata_lv) ||
-	    lv_is_external_origin(metadata_lv) ||
-	    lv_is_virtual(metadata_lv)) {
-		log_error("Pool metadata LV %s is of an unsupported type.",
-			  display_lvname(metadata_lv));
-		return 0;
-	}
-
-	/* FIXME cache pool */
-	if (is_thinpool && pool_is_active(lv)) {
-		/* If any volume referencing pool active - abort here */
-		log_error("Cannot convert pool %s with active volumes.",
-			  display_lvname(lv));
-		return 0;
-	}
-
-	if ((dm_snprintf(meta_name, sizeof(meta_name), "%s%s", lv->name, is_cachepool ? "_cmeta" : "_tmeta") < 0)) {
-                log_error("Failed to create internal lv names, pool name is too long.");
-                return 0;
-        }
-
-	seg = first_seg(lv);
-
-	/* Normally do NOT change chunk size when swapping */
-
-	if (arg_is_set(cmd, chunksize_ARG)) {
-		chunk_size = arg_uint_value(cmd, chunksize_ARG, 0);
-
-		if ((chunk_size != seg->chunk_size) && !dm_list_empty(&lv->segs_using_this_lv)) {
-			if (arg_count(cmd, force_ARG) == PROMPT) {
-				log_error("Chunk size can be only changed with --force. Conversion aborted.");
-				return 0;
-			}
-
-			if (!validate_pool_chunk_size(cmd, seg->segtype, chunk_size))
-				return_0;
-
-			log_warn("WARNING: Changing chunk size %s to %s for %s pool volume.",
-				 display_size(cmd, seg->chunk_size),
-				 display_size(cmd, chunk_size),
-				 display_lvname(lv));
-
-			/* Ok, user has likely some serious reason for this */
-			if (!arg_count(cmd, yes_ARG) &&
-			    yes_no_prompt("Do you really want to change chunk size for %s pool volume? [y/n]: ",
-					  display_lvname(lv)) == 'n') {
-				log_error("Conversion aborted.");
-				return 0;
-			}
-		}
-
-		seg->chunk_size = chunk_size;
-	}
-
-	if (!arg_count(cmd, yes_ARG) &&
-	    yes_no_prompt("Do you want to swap metadata of %s pool with metadata volume %s? [y/n]: ",
-			  display_lvname(lv),
-			  display_lvname(metadata_lv)) == 'n') {
-		log_error("Conversion aborted.");
-		return 0;
-	}
-
-	if (!deactivate_lv(cmd, metadata_lv)) {
-		log_error("Aborting. Failed to deactivate %s.",
-			  display_lvname(metadata_lv));
-		return 0;
-	}
-
-	if (!archive(vg))
-		return_0;
-
-	/* Swap names between old and new metadata LV */
-
-	if (!detach_pool_metadata_lv(seg, &prev_metadata_lv))
-		return_0;
-
-	swap_name = metadata_lv->name;
-
-	if (!lv_rename_update(cmd, metadata_lv, "pvmove_tmeta", 0))
-		return_0;
-
-	/* Give the previous metadata LV the name of the LV replacing it. */
-
-	if (!lv_rename_update(cmd, prev_metadata_lv, swap_name, 0))
-		return_0;
-
-	/* Rename deactivated metadata LV to have _tmeta suffix */
-
-	if (!lv_rename_update(cmd, metadata_lv, meta_name, 0))
-		return_0;
-
-	if (!attach_pool_metadata_lv(seg, metadata_lv))
-		return_0;
-
-	if (!vg_write(vg) || !vg_commit(vg))
-		return_0;
-
-	backup(vg);
-	return 1;
-}
-
-/*
- * Create a new pool LV, using the lv arg as the data sub LV.
- * The metadata sub LV is either a new LV created here, or an
- * existing LV specified by --poolmetadata.
- */
-
-static int _lvconvert_to_pool(struct cmd_context *cmd,
-			      struct logical_volume *lv,
-			      int to_thinpool,
-			      int to_cachepool,
-			      struct dm_list *use_pvh)
-{
-	struct volume_group *vg = lv->vg;
-	struct logical_volume *metadata_lv = NULL;  /* existing or created */
-	struct logical_volume *data_lv;             /* lv arg renamed */
-	struct logical_volume *pool_lv;             /* new lv created here */
-	const char *pool_metadata_name;             /* user-specified lv name */
-	const char *pool_name;                      /* name of original lv arg */
-	char meta_name[NAME_LEN];                   /* generated sub lv name */
-	char data_name[NAME_LEN];                   /* generated sub lv name */
-	struct segment_type *pool_segtype;          /* thinpool or cachepool */
-	struct lv_segment *seg;
-	unsigned int target_attr = ~0;
-	unsigned int passed_args = 0;
-	unsigned int activate_pool;
-	unsigned int zero_metadata;
-	uint64_t meta_size;
-	uint32_t meta_extents;
-	uint32_t chunk_size;
-	int chunk_calc;
-	int r = 0;
-
-	/* for handling lvmlockd cases */
-	char *lockd_data_args = NULL;
-	char *lockd_meta_args = NULL;
-	char *lockd_data_name = NULL;
-	char *lockd_meta_name = NULL;
-	struct id lockd_data_id;
-	struct id lockd_meta_id;
-
-
-	if (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) {
-		log_error(INTERNAL_ERROR "LV %s is already a pool.", display_lvname(lv));
-		return 0;
-	}
-
-	pool_segtype = to_cachepool ? get_segtype_from_string(cmd, SEG_TYPE_NAME_CACHE_POOL) :
-				      get_segtype_from_string(cmd, SEG_TYPE_NAME_THIN_POOL);
-
-	if (!pool_segtype->ops->target_present(cmd, NULL, &target_attr)) {
-		log_error("%s: Required device-mapper target(s) not detected in your kernel.", pool_segtype->name);
-		return 0;
-	}
-
-	/* Allow to have only thinpool active and restore it's active state. */
-	activate_pool = to_thinpool && lv_is_active(lv);
-
-	/* Wipe metadata_lv by default, but allow skipping this for cache pools. */
-	zero_metadata = to_cachepool ? arg_int_value(cmd, zero_ARG, 1) : 1;
-
-	/* An existing LV needs to have its lock freed once it becomes a data LV. */
-	if (is_lockd_type(vg->lock_type) && lv->lock_args) {
-		lockd_data_args = dm_pool_strdup(cmd->mem, lv->lock_args);
-		lockd_data_name = dm_pool_strdup(cmd->mem, lv->name);
-		memcpy(&lockd_data_id, &lv->lvid.id[1], sizeof(struct id));
-	}
-
-	/*
-	 * If an existing LV is to be used as the metadata LV,
-	 * verify that it's in a usable state.  These checks are
-	 * not done by command def rules because this LV is not
-	 * processed by process_each_lv.
-	 */
-
-	if ((pool_metadata_name = arg_str_value(cmd, poolmetadata_ARG, NULL))) {
-		if (!(metadata_lv = find_lv(vg, pool_metadata_name))) {
-			log_error("Unknown pool metadata LV %s.", pool_metadata_name);
-			return 0;
-		}
-
-		/* An existing LV needs to have its lock freed once it becomes a meta LV. */
-		if (is_lockd_type(vg->lock_type) && metadata_lv->lock_args) {
-			lockd_meta_args = dm_pool_strdup(cmd->mem, metadata_lv->lock_args);
-			lockd_meta_name = dm_pool_strdup(cmd->mem, metadata_lv->name);
-			memcpy(&lockd_meta_id, &metadata_lv->lvid.id[1], sizeof(struct id));
-		}
-
-		if (metadata_lv == lv) {
-			log_error("Can't use same LV for pool data and metadata LV %s.",
-				  display_lvname(metadata_lv));
-			return 0;
-		}
-
-		if (!lv_is_visible(metadata_lv)) {
-			log_error("Can't convert internal LV %s.",
-				  display_lvname(metadata_lv));
-			return 0;
-		}
-
-		if (lv_is_locked(metadata_lv)) {
-			log_error("Can't convert locked LV %s.",
-				  display_lvname(metadata_lv));
-			return 0;
-		}
-
-		if (lv_is_mirror(metadata_lv)) {
-			log_error("Mirror logical volumes cannot be used for pool metadata.");
-			log_print_unless_silent("Try \"%s\" segment type instead.", SEG_TYPE_NAME_RAID1);
-			return 0;
-		}
-
-		/* FIXME Tidy up all these type restrictions. */
-		if (lv_is_cache_type(metadata_lv) ||
-		    lv_is_thin_type(metadata_lv) ||
-		    lv_is_cow(metadata_lv) || lv_is_merging_cow(metadata_lv) ||
-		    lv_is_origin(metadata_lv) || lv_is_merging_origin(metadata_lv) ||
-		    lv_is_external_origin(metadata_lv) ||
-		    lv_is_virtual(metadata_lv)) {
-			log_error("Pool metadata LV %s is of an unsupported type.",
-				  display_lvname(metadata_lv));
-			return 0;
-		}
-	}
-
-	/*
-	 * Determine the size of the metadata LV and the chunk size.  When an
-	 * existing LV is to be used for metadata, this introduces some
-	 * constraints/defaults.  When chunk_size=0 and/or meta_extents=0 are
-	 * passed to the "update params" function, defaults are calculated and
-	 * returned.
-	 */
-
-	if (arg_is_set(cmd, chunksize_ARG)) {
-		passed_args |= PASS_ARG_CHUNK_SIZE;
-		chunk_size = arg_uint_value(cmd, chunksize_ARG, 0);
-		if (!validate_pool_chunk_size(cmd, pool_segtype, chunk_size))
-			return_0;
-	} else {
-		/* A default will be chosen by the "update" function. */
-		chunk_size = 0;
-	}
-
-	if (arg_is_set(cmd, poolmetadatasize_ARG)) {
-		meta_size = arg_uint64_value(cmd, poolmetadatasize_ARG, UINT64_C(0));
-		meta_extents = extents_from_size(cmd, meta_size, vg->extent_size);
-		passed_args |= PASS_ARG_POOL_METADATA_SIZE;
-	} else if (metadata_lv) {
-		meta_extents = metadata_lv->le_count;
-		passed_args |= PASS_ARG_POOL_METADATA_SIZE;
-	} else {
-		/* A default will be chosen by the "update" function. */
-		meta_extents = 0;
-	}
-
-	/* Tell the "update" function to ignore these, they are handled below. */
-	passed_args |= PASS_ARG_DISCARDS | PASS_ARG_ZERO;
-
-	/*
-	 * Validate and/or choose defaults for meta_extents and chunk_size,
-	 * this involves some complicated calculations.
-	 */
-
-	if (to_cachepool) {
-		if (!update_cache_pool_params(pool_segtype, vg, target_attr,
-					      passed_args, lv->le_count,
-					      &meta_extents,
-					      &chunk_calc,
-					      &chunk_size))
-			return_0;
-	} else {
-		if (!update_thin_pool_params(pool_segtype, vg, target_attr,
-					     passed_args, lv->le_count,
-					     &meta_extents,
-					     &chunk_calc,
-					     &chunk_size,
-					     NULL, NULL))
-			return_0;
-	}
-
-	if ((uint64_t)chunk_size > ((uint64_t)lv->le_count * vg->extent_size)) {
-		log_error("Pool data LV %s is too small (%s) for specified chunk size (%s).",
-			  display_lvname(lv),
-			  display_size(cmd, (uint64_t)lv->le_count * vg->extent_size),
-			  display_size(cmd, chunk_size));
-		return 0;
-	}
-
-	if (metadata_lv && (meta_extents > metadata_lv->le_count)) {
-		log_error("Pool metadata LV %s is too small (%u extents) for required metadata (%u extents).",
-			  display_lvname(metadata_lv), metadata_lv->le_count, meta_extents);
-		return 0;
-	}
-
-	log_verbose("Pool metadata extents %u chunk_size %u", meta_extents, chunk_size);
-
-
-	/*
-	 * Verify that user wants to use these LVs.
-	 */
-
-	log_warn("WARNING: Converting logical volume %s%s%s to %s pool's data%s %s metadata wiping.",
-		 display_lvname(lv),
-		 metadata_lv ? " and " : "",
-		 metadata_lv ? display_lvname(metadata_lv) : "",
-		 to_cachepool ? "cache" : "thin",
-		 metadata_lv ? " and metadata volumes" : " volume",
-		 zero_metadata ? "with" : "WITHOUT");
-
-	if (zero_metadata)
-		log_warn("THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)");
-	else if (to_cachepool)
-		log_warn("WARNING: Using mismatched cache pool metadata MAY DESTROY YOUR DATA!");
-
-	if (!arg_count(cmd, yes_ARG) &&
-	    yes_no_prompt("Do you really want to convert %s%s%s? [y/n]: ",
-			  display_lvname(lv),
-			  metadata_lv ? " and " : "",
-			  metadata_lv ? display_lvname(metadata_lv) : "") == 'n') {
-		log_error("Conversion aborted.");
-		return 0;
-	}
-
-	/*
-	 * The internal LV names for pool data/meta LVs.
-	 */
-
-	if ((dm_snprintf(meta_name, sizeof(meta_name), "%s%s", lv->name, to_cachepool ? "_cmeta" : "_tmeta") < 0) ||
-	    (dm_snprintf(data_name, sizeof(data_name), "%s%s", lv->name, to_cachepool ? "_cdata" : "_tdata") < 0)) {
-		log_error("Failed to create internal lv names, pool name is too long.");
-		return 0;
-	}
-
-	/*
-	 * If a new metadata LV needs to be created, collect the settings for
-	 * the new LV and create it.
-	 *
-	 * If an existing LV is used for metadata, deactivate/activate/wipe it.
-	 */
-
-	if (!metadata_lv) {
-		uint32_t meta_stripes;
-		uint32_t meta_stripe_size;
-		uint32_t meta_readahead;
-		alloc_policy_t meta_alloc;
-		unsigned meta_stripes_supplied;
-		unsigned meta_stripe_size_supplied;
-
-		if (!get_stripe_params(cmd, get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED),
-				       &meta_stripes,
-				       &meta_stripe_size,
-				       &meta_stripes_supplied,
-				       &meta_stripe_size_supplied))
-			return_0;
-
-		meta_readahead = arg_uint_value(cmd, readahead_ARG, cmd->default_settings.read_ahead);
-		meta_alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT);
-
-		if (!archive(vg))
-			return_0;
-
-		if (!(metadata_lv = alloc_pool_metadata(lv,
-							meta_name,
-							meta_readahead,
-							meta_stripes,
-							meta_stripe_size,
-							meta_extents,
-							meta_alloc,
-							use_pvh)))
-			return_0;
-	} else {
-		if (!deactivate_lv(cmd, metadata_lv)) {
-			log_error("Aborting. Failed to deactivate %s.",
-				  display_lvname(metadata_lv));
-			return 0;
-		}
-
-		if (!archive(vg))
-			return_0;
-
-		if (zero_metadata) {
-			metadata_lv->status |= LV_TEMPORARY;
-			if (!activate_lv_local(cmd, metadata_lv)) {
-				log_error("Aborting. Failed to activate metadata lv.");
-				return 0;
-			}
-
-			if (!wipe_lv(metadata_lv, (struct wipe_params) { .do_zero = 1 })) {
-				log_error("Aborting. Failed to wipe metadata lv.");
-				return 0;
-			}
-		}
-	}
-
-	/*
-	 * Deactivate the data LV and metadata LV.
-	 * We are changing target type, so deactivate first.
-	 */
-
-	if (!deactivate_lv(cmd, metadata_lv)) {
-		log_error("Aborting. Failed to deactivate metadata lv. "
-			  "Manual intervention required.");
-		return 0;
-	}
-
-	if (!deactivate_lv(cmd, lv)) {
-		log_error("Aborting. Failed to deactivate logical volume %s.",
-			  display_lvname(lv));
-		return 0;
-	}
-
-	/*
-	 * When the LV referenced by the original function arg "lv"
-	 * is renamed, it is then referenced as "data_lv".
-	 *
-	 * pool_name    pool name taken from lv arg
-	 * data_name    sub lv name, generated
-	 * meta_name    sub lv name, generated
-	 *
-	 * pool_lv      new lv for pool object, created here
-	 * data_lv      sub lv, was lv arg, now renamed
-	 * metadata_lv  sub lv, existing or created here
-	 */
-
-	data_lv = lv;
-	pool_name = lv->name; /* Use original LV name for pool name */
-
-	/*
-	 * Rename the original LV arg to the internal data LV naming scheme.
-	 *
-	 * Since we wish to have underlaying devs to match _[ct]data
-	 * rename data LV to match pool LV subtree first,
-	 * also checks for visible LV.
-	 *
-	 * FIXME: any more types prohibited here?
-	 */
-
-	if (!lv_rename_update(cmd, data_lv, data_name, 0))
-		return_0;
-
-	/*
-	 * Create LV structures for the new pool LV object,
-	 * and connect it to the data/meta LVs.
-	 */
-
-	if (!(pool_lv = lv_create_empty(pool_name, NULL,
-					(to_cachepool ? CACHE_POOL : THIN_POOL) | VISIBLE_LV | LVM_READ | LVM_WRITE,
-					ALLOC_INHERIT, vg))) {
-		log_error("Creation of pool LV failed.");
-		return 0;
-	}
-
-	/* Allocate a new pool segment */
-	if (!(seg = alloc_lv_segment(pool_segtype, pool_lv, 0, data_lv->le_count,
-				     pool_lv->status, 0, NULL, 1,
-				     data_lv->le_count, 0, 0, 0, NULL)))
-		return_0;
-
-	/* Add the new segment to the layer LV */
-	dm_list_add(&pool_lv->segments, &seg->list);
-	pool_lv->le_count = data_lv->le_count;
-	pool_lv->size = data_lv->size;
-
-	if (!attach_pool_data_lv(seg, data_lv))
-		return_0;
-
-	/*
-	 * Create a new lock for a thin pool LV.  A cache pool LV has no lock.
-	 * Locks are removed from existing LVs that are being converted to
-	 * data and meta LVs (they are unlocked and deleted below.)
-	 */
-	if (is_lockd_type(vg->lock_type)) {
-		if (to_cachepool) {
-			data_lv->lock_args = NULL;
-			metadata_lv->lock_args = NULL;
-		} else {
-			data_lv->lock_args = NULL;
-			metadata_lv->lock_args = NULL;
-
-			if (!strcmp(vg->lock_type, "sanlock"))
-				pool_lv->lock_args = "pending";
-			else if (!strcmp(vg->lock_type, "dlm"))
-				pool_lv->lock_args = "dlm";
-			/* The lock_args will be set in vg_write(). */
-		}
-	}
-
-	/*
-	 * Apply settings to the new pool seg, from command line, from
-	 * defaults, sometimes adjusted.
-	 */
-
-	seg->transaction_id = 0;
-	seg->chunk_size = chunk_size;
-
-	if (to_cachepool) {
-		cache_mode_t cache_mode = 0;
-		const char *policy_name = NULL;
-		struct dm_config_tree *policy_settings = NULL;
-
-		if (!get_cache_params(cmd, &cache_mode, &policy_name, &policy_settings))
-			return_0;
-
-		if (cache_mode &&
-		    !cache_set_cache_mode(seg, cache_mode))
-			return_0;
-
-		if ((policy_name || policy_settings) &&
-		    !cache_set_policy(seg, policy_name, policy_settings))
-			return_0;
-
-		if (policy_settings)
-			dm_config_destroy(policy_settings);
-	} else {
-		const char *discards_name;
-
-		if (arg_is_set(cmd, zero_ARG))
-			seg->zero_new_blocks = arg_int_value(cmd, zero_ARG, 0);
-		else
-			seg->zero_new_blocks = find_config_tree_bool(cmd, allocation_thin_pool_zero_CFG, vg->profile);
-
-		if (arg_is_set(cmd, discards_ARG))
-			seg->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN);
-		else {
-			if (!(discards_name = find_config_tree_str(cmd, allocation_thin_pool_discards_CFG, vg->profile)))
-				return_0;
-			if (!set_pool_discards(&seg->discards, discards_name))
-				return_0;
-		}
-	}
-
-	/*
-	 * Rename deactivated metadata LV to have _tmeta suffix.
-	 * Implicit checks if metadata_lv is visible.
-	 */
-	if (pool_metadata_name &&
-	    !lv_rename_update(cmd, metadata_lv, meta_name, 0))
-		return_0;
-
-	if (!attach_pool_metadata_lv(seg, metadata_lv))
-		return_0;
-
-	if (!handle_pool_metadata_spare(vg,
-					metadata_lv->le_count,
-					use_pvh,
-					arg_int_value(cmd, poolmetadataspare_ARG, DEFAULT_POOL_METADATA_SPARE)))
-		return_0;
-
-	if (!vg_write(vg) || !vg_commit(vg))
-		return_0;
-
-	if (seg->zero_new_blocks &&
-	    seg->chunk_size >= DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE * 2)
-		log_warn("WARNING: Pool zeroing and large %s chunk size slows down provisioning.",
-			 display_size(cmd, seg->chunk_size));
-
-	if (activate_pool && !lockd_lv(cmd, pool_lv, "ex", LDLV_PERSISTENT)) {
-		log_error("Failed to lock pool LV %s.", display_lvname(pool_lv));
-		goto out;
-	}
-
-	if (activate_pool &&
-	    !activate_lv_excl(cmd, pool_lv)) {
-		log_error("Failed to activate pool logical volume %s.",
-			  display_lvname(pool_lv));
-		/* Deactivate subvolumes */
-		if (!deactivate_lv(cmd, seg_lv(seg, 0)))
-			log_error("Failed to deactivate pool data logical volume %s.",
-				  display_lvname(seg_lv(seg, 0)));
-		if (!deactivate_lv(cmd, seg->metadata_lv))
-			log_error("Failed to deactivate pool metadata logical volume %s.",
-				  display_lvname(seg->metadata_lv));
-		goto out;
-	}
-
-	r = 1;
-
-out:
-	backup(vg);
-
-	if (r)
-		log_print_unless_silent("Converted %s to %s pool.",
-					display_lvname(lv),
-					to_cachepool ? "cache" : "thin");
-
-	/*
-	 * Unlock and free the locks from existing LVs that became pool data
-	 * and meta LVs.
-	 */
-	if (lockd_data_name) {
-		if (!lockd_lv_name(cmd, vg, lockd_data_name, &lockd_data_id, lockd_data_args, "un", LDLV_PERSISTENT))
-			log_error("Failed to unlock pool data LV %s/%s", vg->name, lockd_data_name);
-		lockd_free_lv(cmd, vg, lockd_data_name, &lockd_data_id, lockd_data_args);
-	}
-
-	if (lockd_meta_name) {
-		if (!lockd_lv_name(cmd, vg, lockd_meta_name, &lockd_meta_id, lockd_meta_args, "un", LDLV_PERSISTENT))
-			log_error("Failed to unlock pool metadata LV %s/%s", vg->name, lockd_meta_name);
-		lockd_free_lv(cmd, vg, lockd_meta_name, &lockd_meta_id, lockd_meta_args);
-	}
-
-	return r;
-#if 0
-revert_new_lv:
-	/* TBD */
-	if (!pool_metadata_lv_name) {
-		if (!deactivate_lv(cmd, metadata_lv)) {
-			log_error("Failed to deactivate metadata lv.");
-			return 0;
-		}
-		if (!lv_remove(metadata_lv) || !vg_write(vg) || !vg_commit(vg))
-			log_error("Manual intervention may be required to remove "
-				  "abandoned LV(s) before retrying.");
-		else
-			backup(vg);
-	}
-
-	return 0;
-#endif
-}
-
-/*
- * Convert origin into a cache LV by attaching a cache pool.
- */
-static int _lvconvert_cache(struct cmd_context *cmd,
-			    struct logical_volume *origin_lv,
-			    struct lvconvert_params *lp)
-{
-	struct logical_volume *pool_lv = lp->pool_data_lv;
-	struct logical_volume *cache_lv;
-
-	if (!validate_lv_cache_create_pool(pool_lv))
-		return_0;
-
-	if (!archive(origin_lv->vg))
-		return_0;
-
-	if (!(cache_lv = lv_cache_create(pool_lv, origin_lv)))
-		return_0;
-
-	if (!cache_set_cache_mode(first_seg(cache_lv), lp->cache_mode))
-		return_0;
-
-	if (!cache_set_policy(first_seg(cache_lv), lp->policy_name, lp->policy_settings))
-		return_0;
-
-	cache_check_for_warns(first_seg(cache_lv));
-
-	if (!lv_update_and_reload(cache_lv))
-		return_0;
-
-	log_print_unless_silent("Logical volume %s is now cached.",
-				display_lvname(cache_lv));
-
-	return 1;
-}
-
-static int _lvconvert_to_cache_vol(struct cmd_context *cmd,
-			    struct logical_volume *lv,
-			    struct logical_volume *cachepool_lv)
-{
-	struct logical_volume *cache_lv;
-	cache_mode_t cache_mode = 0;
-	const char *policy_name = NULL;
-	struct dm_config_tree *policy_settings = NULL;
-
-	if (!validate_lv_cache_create_pool(cachepool_lv))
-		return_0;
-
-	if (!get_cache_params(cmd, &cache_mode, &policy_name, &policy_settings))
-		return_0;
-
-	if (!archive(lv->vg))
-		return_0;
-
-	if (!(cache_lv = lv_cache_create(cachepool_lv, lv)))
-		return_0;
-
-	if (!cache_set_cache_mode(first_seg(cache_lv), cache_mode))
-		return_0;
-
-	if (!cache_set_policy(first_seg(cache_lv), policy_name, policy_settings))
-		return_0;
-
-	if (policy_settings)
-		dm_config_destroy(policy_settings);
-
-	cache_check_for_warns(first_seg(cache_lv));
-
-	if (!lv_update_and_reload(cache_lv))
-		return_0;
-
-	log_print_unless_silent("Logical volume %s is now cached.",
-				display_lvname(cache_lv));
-
-	return 1;
-}
-
-/*
- * Functions called to perform a specific operation on a specific LV type.
- *
- * _convert_<lvtype>_<operation>
- *
- * For cases where an operation does not apply to the LV itself, but
- * is implicitly redirected to a sub-LV, these functions locate the
- * correct sub-LV and call the operation on that sub-LV.  If a sub-LV
- * of the proper type is not found, these functions report the error.
- *
- * FIXME: the _lvconvert_foo() functions can be cleaned up since they
- * are now only called for valid combinations of LV type and operation.
- * After that happens, the code remaining in those functions can be
- * moved into the _convert_lvtype_operation() functions below.
- */
-
-/*
- * Separate a COW snapshot LV from its origin.
- * lvconvert --splitsnapshot LV
- */
-static int _convert_cow_snapshot_splitsnapshot(struct cmd_context *cmd, struct logical_volume *lv,
-					       struct lvconvert_params *lp)
-{
-	return _lvconvert_splitsnapshot(cmd, lv);
-}
-
-/*
- * Merge a COW snapshot LV into its origin.
- * lvconvert --merge LV
- */
-static int _convert_cow_snapshot_merge(struct cmd_context *cmd, struct logical_volume *lv,
-				       struct lvconvert_params *lp)
-{
-	/* return _lvconvert_merge_old_snapshot(cmd, lv, lp); */
-}
-
-/*
- * Merge a snapshot thin LV into its origin.
- * lvconvert --merge LV
- */
-
-static int _convert_thin_volume_merge(struct cmd_context *cmd, struct logical_volume *lv,
-				      struct lvconvert_params *lp)
-{
-	return _lvconvert_merge_thin_snapshot(cmd, lv);
-}
-
-/*
- * Split and preserve a cache pool from the data portion of a thin pool LV.
- * lvconvert --splitcache LV
- */
-static int _convert_thin_pool_splitcache(struct cmd_context *cmd, struct logical_volume *lv,
-					 struct lvconvert_params *lp)
-{
-	struct logical_volume *sublv1;
-
-	sublv1 = seg_lv(first_seg(lv), 0); /* cached _tdata ? */
-
-	if (!lv_is_cache(sublv1)) {
-		log_error("Sub LV %s must be cache.", display_lvname(sublv1));
-		return 0;
-	}
-
-	/* return _lvconvert_split_cached(cmd, sublv1); */
-	return 0;
-}
-
-/*
- * Split and remove a cache pool from the data portion of a thin pool LV.
- * lvconvert --uncache LV
- */
-static int _convert_thin_pool_uncache(struct cmd_context *cmd, struct logical_volume *lv,
-				      struct lvconvert_params *lp)
-{
-	struct logical_volume *sublv1 = NULL;
-
-	sublv1 = seg_lv(first_seg(lv), 0); /* cached _tdata ? */
-
-	if (!lv_is_cache(sublv1)) {
-		log_error("Sub LV %s must be cache.", display_lvname(sublv1));
-		return 0;
-	}
-
-	/* return _lvconvert_uncache(cmd, sublv1, lp); */
-	return 0;
-}
-
-/*
- * Convert the data portion of a thin pool LV to a cache LV.
- * lvconvert --type cache LV
- *
- * Required options:
- * --cachepool LV
- *
- * Auxiliary operation:
- * Converts the --cachepool arg to a cache pool if it is not already.
- *
- * Alternate syntax:
- * lvconvert --cache LV
- */
-static int _convert_thin_pool_cache(struct cmd_context *cmd, struct logical_volume *lv,
-				    struct lvconvert_params *lp)
-{
-	/* lvconvert --type cache includes an implicit conversion of the cachepool arg to type cache-pool. */
-	if (!_lvconvert_pool(cmd, lv, lp)) {
-		log_error("Implicit conversion of --cachepool arg to type cache-pool failed.");
-		return 0;
-	}
-
-	/* Do we need to grab the tdata sub LV to pass on? */
-
-	return _lvconvert_cache(cmd, lv, lp);
-}
-
-/*
- * Replace the metadata LV in a thin pool LV.
- * lvconvert --poolmetadata NewLV --thinpool LV
- * FIXME: this will change so --swap-poolmetadata defines the operation.
- * FIXME: should be lvconvert --swap-poolmetadata NewLV LV
- */
-static int _convert_thin_pool_swapmetadata(struct cmd_context *cmd, struct logical_volume *lv,
-					   struct lvconvert_params *lp)
-{
-	return _lvconvert_pool(cmd, lv, lp);
-}
-
-/*
- * Split and preserve a cache pool from a cache LV.
- * lvconvert --splitcache LV
- */
-static int _convert_cache_volume_splitcache(struct cmd_context *cmd, struct logical_volume *lv,
-					    struct lvconvert_params *lp)
-{
-	/* return _lvconvert_split_cached(cmd, lv); */
-	return 0;
-}
-
-/*
- * Split and remove a cache pool from a cache LV.
- * lvconvert --uncache LV
- */
-static int _convert_cache_volume_uncache(struct cmd_context *cmd, struct logical_volume *lv,
-					 struct lvconvert_params *lp)
-{
-	/* return _lvconvert_uncache(cmd, lv, lp); */
-	return 0;
-}
-
-/*
- * Split images from the raid1|mirror origin of a cache LV and use them to create a new LV.
- * lvconvert --splitmirrors Number LV
- *
- * Required options:
- * --trackchanges | --name Name
- */
-static int _convert_cache_volume_splitmirrors(struct cmd_context *cmd, struct logical_volume *lv,
-					     struct lvconvert_params *lp)
-{
-	struct logical_volume *sublv1;
-
-	sublv1 = seg_lv(first_seg(lv), 0);
-
-	if (lv_is_raid(sublv1))
-		return _lvconvert_raid(sublv1, lp);
-
-	if (lv_is_mirror(sublv1))
-		return _lvconvert_mirrors(cmd, lv, lp);
-
-	log_error("Sub LV %s must be raid or mirror.", display_lvname(sublv1));
-	return 0;
-}
-
-/*
- * Convert a cache LV to a thin pool (using the cache LV for thin pool data).
- * lvconvert --type thin-pool LV
- *
- * Convert a cache LV to a thin volume with cached external origin using given
- * thinpool tpLV (when not yet Thinpool convert it to thin-pool first).
- * Conversion is 2-step process in this case.
- * Only writethrough cacheLV can be converted as external origin is read-only.
- * lvconvert --thin cacheLV --thinpool tpLV
- *
- * Alternate syntax:
- * This is equivalent to above, but not preferred because it's ambiguous and inconsistent.
- * lvconvert --thinpool LV
- */
-static int _convert_cache_volume_thin_pool(struct cmd_context *cmd, struct logical_volume *lv,
-					   struct lvconvert_params *lp)
-{
-	int is_clean;
-	const struct lv_segment *pool_seg;
-
-	if (!_lvconvert_pool(cmd, lv, lp))
-		return_0;
-
-	if (lv_is_cache(lv) && !lv_is_pool_data(lv)) {
-		pool_seg = first_seg(first_seg(lv)->pool_lv);
-		if (pool_seg->cache_mode != CACHE_MODE_WRITETHROUGH) {
-			log_error("Cannot convert cache volume %s with %s cache mode to external origin.",
-				  display_lvname(lv),
-				  get_cache_mode_name(pool_seg));
-			log_error("To proceed, run 'lvchange --cachemode writethrough %s'.",
-				  display_lvname(lv));
-			return 0;
-		}
-
-		if (!lv_cache_wait_for_clean(lv, &is_clean))
-			return_0;
-
-		if (!is_clean) {
-			log_error("Cache %s is not clean, refusing to convert to external origin.",
-				  display_lvname(lv));
-			return 0;
-		}
-
-		if (!_lvconvert_thin(cmd, lv, lp))
-			return_0;
-	}
-
-	return 1;
-}
-
-/*
- * Convert/Recombine  cacheLV to be an origin for snapshot
- * lvconvert --type snapshot cacheLV snapshotLV
- */
-static int _convert_cache_volume_snapshot(struct cmd_context *cmd, struct logical_volume *lv,
-					  struct lvconvert_params *lp)
-{
-	return _lvconvert_snapshot(cmd, lv, lp->origin_name);
-}
-
-/*
- * Split a cache volume from a cache pool LV.
- * lvconvert --splitcache LV
- */
-static int _convert_cache_pool_splitcache(struct cmd_context *cmd, struct logical_volume *lv,
-					  struct lvconvert_params *lp)
-{
-	struct logical_volume *sublv1;
-	struct lv_segment *seg;
-
-	/* When passed used cache-pool of used cached LV -> split cached LV */
-
-	if ((dm_list_size(&lv->segs_using_this_lv) == 1) &&
-	    (seg = get_only_segment_using_this_lv(lv)) &&
-	    seg_is_cache(seg))
-		sublv1 = seg->lv;
-	else {
-		log_error("Sub LV of cache type not found.");
-		return 0;
-	}
-
-	if (!lv_is_cache(sublv1)) {
-		log_error("Sub LV %s must be cache.", display_lvname(sublv1));
-		return 0;
-	}
-
-	/* return _lvconvert_split_cached(cmd, sublv1); */
-	return 0;
-}
-
-/*
- * Replace the metadata LV in a cache pool LV.
- * lvconvert --poolmetadata NewLV --cachepool LV
- * FIXME: this will change so --swap-poolmetadata defines the operation.
- * FIXME: should be lvconvert --swap-poolmetadata NewLV LV
- */
-static int _convert_cache_pool_swapmetadata(struct cmd_context *cmd, struct logical_volume *lv,
-					    struct lvconvert_params *lp)
-{
-	return _lvconvert_pool(cmd, lv, lp);
-}
-
-/*
- * Change the number of images in a mirror LV.
- * lvconvert --mirrors Number LV
- */
-static int _convert_mirror_number(struct cmd_context *cmd, struct logical_volume *lv,
-				  struct lvconvert_params *lp)
-{
-	return _lvconvert_mirrors(cmd, lv, lp);
-}
-
-/*
- * Split images from a mirror LV and use them to create a new LV.
- * lvconvert --splitmirrors Number LV
- *
- * Required options:
- * --name Name
- */
-
-static int _convert_mirror_splitmirrors(struct cmd_context *cmd, struct logical_volume *lv,
-					struct lvconvert_params *lp)
-{
-	return _lvconvert_mirrors(cmd, lv, lp);
-}
-
-/*
- * Change the type of log used by a mirror LV.
- * lvconvert --mirrorlog Type LV
- */
-static int _convert_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
-				  struct lvconvert_params *lp)
-{
-	return _lvconvert_mirrors(cmd, lv, lp);
-}
-
-/*
- * Convert mirror LV to linear LV.
- * lvconvert --type linear LV
- *
- * Alternate syntax:
- * lvconvert --mirrors 0 LV
- */
-static int _convert_mirror_linear(struct cmd_context *cmd, struct logical_volume *lv,
-				  struct lvconvert_params *lp)
-{
-	return _lvconvert_mirrors(cmd, lv, lp);
-}
-
-/*
- * Convert mirror LV to raid1 LV.
- * lvconvert --type raid1 LV
- */
-static int _convert_mirror_raid(struct cmd_context *cmd, struct logical_volume *lv,
-				struct lvconvert_params *lp)
-{
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Change the number of images in a raid1 LV.
- * lvconvert --mirrors Number LV
- */
-static int _convert_raid_number(struct cmd_context *cmd, struct logical_volume *lv,
-				struct lvconvert_params *lp)
-{
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Split images from a raid1 LV and use them to create a new LV.
- * lvconvert --splitmirrors Number LV
- *
- * Required options:
- * --trackchanges | --name Name
- */
-static int _convert_raid_splitmirrors(struct cmd_context *cmd, struct logical_volume *lv,
-				      struct lvconvert_params *lp)
-{
-	/* FIXME: split the splitmirrors section out of _lvconvert_raid and call it here. */
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Merge a raid1 LV into originalLV if the raid1 LV was
- * previously split from the originalLV using --trackchanges.
- * lvconvert --merge LV
- */
-static int _convert_raid_merge(struct cmd_context *cmd, struct logical_volume *lv,
-			       struct lvconvert_params *lp)
-{
-	/* FIXME: split the merge section out of _lvconvert_raid and call it here. */
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Combine a raid* LV with a snapshot LV that was previously
- * split from the raid* LV using --splitsnapshot.
- * lvconvert --type snapshot LV SnapshotLV
- *
- * Alternate syntax:
- * lvconvert --snapshot LV SnapshotLV
- */
-static int _convert_raid_snapshot(struct cmd_context *cmd, struct logical_volume *lv,
-				  struct lvconvert_params *lp)
-{
-	return _lvconvert_snapshot(cmd, lv, lp->origin_name);
-}
-
-/*
- * Convert a raid* LV to a thin LV with an external origin.
- * lvconvert --type thin LV
- *
- * Required options:
- * --thinpool LV
- *
- * Auxiliary operation:
- * Converts the --thinpool arg to a thin pool if it is not already.
- *
- * Alternate syntax:
- * lvconvert --thin LV
- */
-static int _convert_raid_thin(struct cmd_context *cmd, struct logical_volume *lv,
-			      struct lvconvert_params *lp)
-{
-	/* lvconvert --thin includes an implicit conversion of the thinpool arg to type thin-pool. */
-	if (!_lvconvert_pool(cmd, lv, lp)) {
-		log_error("Implicit conversion of --thinpool arg to type thin-pool failed.");
-		return 0;
-	}
-
-	return _lvconvert_thin(cmd, lv, lp);
-}
-
-/*
- * Convert a raid* LV to a cache LV.
- * lvconvert --type cache LV
- *
- * Required options:
- * --cachepool LV
- *
- * Auxiliary operation:
- * Converts the --cachepool arg to a cache pool if it is not already.
- *
- * Alternate syntax:
- * lvconvert --cache LV
- */
-static int _convert_raid_cache(struct cmd_context *cmd, struct logical_volume *lv,
-			       struct lvconvert_params *lp)
-{
-	/* lvconvert --type cache includes an implicit conversion of the cachepool arg to type cache-pool. */
-	if (!_lvconvert_pool(cmd, lv, lp)) {
-		log_error("Implicit conversion of --cachepool arg to type cache-pool failed.");
-		return 0;
-	}
-
-	return _lvconvert_cache(cmd, lv, lp);
-}
-
-/*
- * Convert a raid* LV to a thin-pool LV.
- * lvconvert --type thin-pool LV
- *
- * Alternate syntax:
- * This is equivalent to above, but not preferred because it's ambiguous and inconsistent.
- * lvconvert --thinpool LV
- */
-static int _convert_raid_thin_pool(struct cmd_context *cmd, struct logical_volume *lv,
-				   struct lvconvert_params *lp)
-{
-	return _lvconvert_pool(cmd, lv, lp);
-}
-
-/*
- * Convert a raid* LV to cache-pool LV.
- * lvconvert --type cache-pool LV
- */
-static int _convert_raid_cache_pool(struct cmd_context *cmd, struct logical_volume *lv,
-				    struct lvconvert_params *lp)
-{
-	return _lvconvert_pool(cmd, lv, lp);
-}
-
-/*
- * Convert a raid* LV to use a different raid level.
- * lvconvert --type raid* LV
- */
-static int _convert_raid_raid(struct cmd_context *cmd, struct logical_volume *lv,
-			      struct lvconvert_params *lp)
-{
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Convert a raid* LV to a mirror LV.
- * lvconvert --type mirror LV
- */
-static int _convert_raid_mirror(struct cmd_context *cmd, struct logical_volume *lv,
-			      struct lvconvert_params *lp)
-{
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Convert a raid* LV to a striped LV.
- * lvconvert --type striped LV
- */
-static int _convert_raid_striped(struct cmd_context *cmd, struct logical_volume *lv,
-				 struct lvconvert_params *lp)
-{
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Convert a raid* LV to a linear LV.
- * lvconvert --type linear LV
- */
-static int _convert_raid_linear(struct cmd_context *cmd, struct logical_volume *lv,
-				struct lvconvert_params *lp)
-{
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Merge a striped/linear LV into a raid1 LV if the striped/linear LV was
- * previously split from the raid1 LV using --trackchanges.
- * lvconvert --merge LV
- */
-static int _convert_striped_merge(struct cmd_context *cmd, struct logical_volume *lv,
-				  struct lvconvert_params *lp)
-{
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Combine a linear/striped LV with a snapshot LV that was previously
- * split from the linear/striped LV using --splitsnapshot.
- * lvconvert --type snapshot LV SnapshotLV
- *
- * Alternate syntax:
- * lvconvert --snapshot LV SnapshotLV
- */
-static int _convert_striped_snapshot(struct cmd_context *cmd, struct logical_volume *lv,
-				     struct lvconvert_params *lp)
-{
-	return _lvconvert_snapshot(cmd, lv, lp->origin_name);
-}
-
-/*
- * Convert a striped/linear LV to a thin LV with an external origin.
- * lvconvert --type thin LV
- *
- * Required options:
- * --thinpool LV
- *
- * Auxiliary operation:
- * Converts the --thinpool arg to a thin pool if it is not already.
- *
- * Alternate syntax:
- * lvconvert --thin LV
- */
-static int _convert_striped_thin(struct cmd_context *cmd, struct logical_volume *lv,
-				 struct lvconvert_params *lp)
-{
-	/* lvconvert --thin includes an implicit conversion of the thinpool arg to type thin-pool. */
-	if (!_lvconvert_pool(cmd, lv, lp)) {
-		log_error("Conversion of --thinpool arg to type thin-pool failed.");
-		return 0;
-	}
-
-	return _lvconvert_thin(cmd, lv, lp);
-}
-
-/*
- * Convert a striped/linear LV to a cache LV.
- * lvconvert --type cache LV
- *
- * Required options:
- * --cachepool LV
- *
- * Auxiliary operation:
- * Converts the --cachepool arg to a cache pool if it is not already.
- *
- * Alternate syntax:
- * lvconvert --cache LV
- */
-
-static int _convert_striped_cache(struct cmd_context *cmd, struct logical_volume *lv,
-				  struct lvconvert_params *lp)
-{
-	/* lvconvert --cache includes an implicit conversion of the cachepool arg to type cache-pool. */
-	if (!_lvconvert_pool(cmd, lv, lp)) {
-		log_error("Conversion of --cachepool arg to type cache-pool failed.");
-		return 0;
-	}
-
-	return _lvconvert_cache(cmd, lv, lp);
-}
-
-/*
- * Convert a striped/linear LV to a thin-pool LV.
- * lvconvert --type thin-pool LV
- *
- * Alternate syntax:
- * This is equivalent to above, but not preferred because it's ambiguous and inconsistent.
- * lvconvert --thinpool LV
- */
-static int _convert_striped_thin_pool(struct cmd_context *cmd, struct logical_volume *lv,
-				      struct lvconvert_params *lp)
-{
-	return _lvconvert_pool(cmd, lv, lp);
-}
-
-/*
- * Convert a striped/linear LV to a cache-pool LV.
- * lvconvert --type cache-pool LV
- */
-static int _convert_striped_cache_pool(struct cmd_context *cmd, struct logical_volume *lv,
-				       struct lvconvert_params *lp)
-{
-	return _lvconvert_pool(cmd, lv, lp);
-}
-
-/*
- * Convert a striped/linear LV to a mirror LV.
- * lvconvert --type mirror LV
- *
- * Required options:
- * --mirrors Number
- *
- * Alternate syntax:
- * This is equivalent to above when global/mirror_segtype_default="mirror".
- * lvconvert --mirrors Number LV
- */
-static int _convert_striped_mirror(struct cmd_context *cmd, struct logical_volume *lv,
-				   struct lvconvert_params *lp)
-{
-	return _lvconvert_mirrors(cmd, lv, lp);
-}
-
-/*
- * Convert a striped/linear LV to a raid* LV.
- * lvconvert --type raid* LV
- *
- * Required options:
- * --mirrors Number
- *
- * Alternate syntax:
- * This is equivalent to above when global/mirror_segtype_default="raid1".
- * lvconvert --mirrors Number LV
- */
-static int _convert_striped_raid(struct cmd_context *cmd, struct logical_volume *lv,
-				 struct lvconvert_params *lp)
-{
-	return _lvconvert_raid(lv, lp);
-}
-
-/*
- * Functions called to perform all valid operations on a given LV type.
- *
- * _convert_<lvtype>
- */
-static int _convert_cow_snapshot(struct cmd_context *cmd, struct logical_volume *lv,
-				 struct lvconvert_params *lp)
-{
-	if (lp->splitsnapshot)
-		return _convert_cow_snapshot_splitsnapshot(cmd, lv, lp);
-
-	/* FIXME: add --merge-snapshot to make this distinct from --merge-mirror. */
-	if (lp->merge)
-		return _convert_cow_snapshot_merge(cmd, lv, lp);
-
-	log_error("Operation not permitted on COW snapshot LV %s.", display_lvname(lv));
-	log_error("Operations permitted on a COW snapshot LV are:\n"
-		  "  --splitsnapshot\n"
-		  "  --merge\n");
-	return 0;
-}
-
-static int _convert_thin_volume(struct cmd_context *cmd, struct logical_volume *lv,
-				struct lvconvert_params *lp)
-{
-	/* FIXME: add --merge-snapshot to make this distinct from --merge-mirror. */
-	if (lp->merge)
-		return _convert_thin_volume_merge(cmd, lv, lp);
-
-	log_error("Operation not permitted on thin LV %s.", display_lvname(lv));
-	log_error("Operations permitted on a thin LV are:\n"
-		  "  --merge\n");
-	return 0;
-}
-
-static int _convert_thin_pool(struct cmd_context *cmd, struct logical_volume *lv,
-			      struct lvconvert_params *lp)
-{
-	if (lp->splitcache)
-		return _convert_thin_pool_splitcache(cmd, lv, lp);
-
-	if (lp->split)
-		return _convert_thin_pool_splitcache(cmd, lv, lp);
-
-	if (lp->uncache)
-		return _convert_thin_pool_uncache(cmd, lv, lp);
-
-	if (lp->cache)
-		return _convert_thin_pool_cache(cmd, lv, lp);
-
-	/* FIXME: swapping the thin pool metadata LV needs a specific option like --swapmetadata */
-	if (arg_is_set(cmd, poolmetadata_ARG))
-		return _convert_thin_pool_swapmetadata(cmd, lv, lp);
-
-	/* FIXME: add --swapmetadata to list of permitted operations. */
-
-	log_error("Operation not permitted on thin pool LV %s.", display_lvname(lv));
-	log_error("Operations permitted on a thin pool LV are:\n"
-		  "  --splitcache  (operates on cache sub LV)\n"
-		  "  --uncache     (operates on cache sub LV)\n"
-		  "  --type cache  (operates on data sub LV)\n"
-		  "  --repair\n");
-	return 0;
-}
-
-static int _convert_cache_volume(struct cmd_context *cmd, struct logical_volume *lv,
-				 struct lvconvert_params *lp)
-{
-	if (lp->splitcache)
-		return _convert_cache_volume_splitcache(cmd, lv, lp);
-
-	if (lp->split)
-		return _convert_cache_volume_splitcache(cmd, lv, lp);
-
-	if (lp->uncache)
-		return _convert_cache_volume_uncache(cmd, lv, lp);
-
-	if (arg_is_set(cmd, splitmirrors_ARG))
-		return _convert_cache_volume_splitmirrors(cmd, lv, lp);
-
-	if (!strcmp(lp->type_str, SEG_TYPE_NAME_THIN_POOL) ||
-	    arg_is_set(cmd, thinpool_ARG))
-		return _convert_cache_volume_thin_pool(cmd, lv, lp);
-
-	if (!strcmp(lp->type_str, SEG_TYPE_NAME_SNAPSHOT) ||
-	    arg_is_set(cmd, snapshot_ARG))
-		return _convert_cache_volume_snapshot(cmd, lv, lp);
-
-	/* The --thinpool alternative for --type thin-pool is not preferred, so not shown. */
-
-	log_error("Operation not permitted on cache LV %s.", display_lvname(lv));
-	log_error("Operations permitted on a cache LV are:\n"
-		  "  --splitcache\n"
-		  "  --uncache\n"
-		  "  --splitmirrors (operates on mirror or raid sub LV)\n"
-		  "  --type thin-pool\n");
-	return 0;
-}
-
-static int _convert_cache_pool(struct cmd_context *cmd, struct logical_volume *lv,
-			       struct lvconvert_params *lp)
-{
-	if (lp->splitcache)
-		return _convert_cache_pool_splitcache(cmd, lv, lp);
-
-	if (lp->split)
-		return _convert_cache_pool_splitcache(cmd, lv, lp);
+		}
+	}
 
-	/* FIXME: swapping the cache pool metadata LV needs a specific option like --swapmetadata */
-	if (arg_is_set(cmd, poolmetadata_ARG))
-		return _convert_cache_pool_swapmetadata(cmd, lv, lp);
+	/*
+	 * Apply settings to the new pool seg, from command line, from
+	 * defaults, sometimes adjusted.
+	 */
 
-	/* FIXME: add --swapmetadata to list of permitted operations. */
+	seg->transaction_id = 0;
+	seg->chunk_size = chunk_size;
 
-	log_error("Operation not permitted on cache pool LV %s.", display_lvname(lv));
-	log_error("Operations permitted on a cache pool LV are:\n"
-		  "  --splitcache    (operates on cache LV)\n");
-	return 0;
-}
+	if (to_cachepool) {
+		cache_mode_t cache_mode = 0;
+		const char *policy_name = NULL;
+		struct dm_config_tree *policy_settings = NULL;
 
-static int _convert_mirror(struct cmd_context *cmd, struct logical_volume *lv,
-			   struct lvconvert_params *lp)
-{
-	if (arg_is_set(cmd, mirrors_ARG))
-		return _convert_mirror_number(cmd, lv, lp);
+		if (!get_cache_params(cmd, &cache_mode, &policy_name, &policy_settings))
+			return_0;
 
-	if (arg_is_set(cmd, splitmirrors_ARG))
-		return _convert_mirror_splitmirrors(cmd, lv, lp);
+		if (cache_mode &&
+		    !cache_set_cache_mode(seg, cache_mode))
+			return_0;
 
-	if (arg_is_set(cmd, mirrorlog_ARG) || arg_is_set(cmd, corelog_ARG))
-		return _convert_mirror_log(cmd, lv, lp);
+		if ((policy_name || policy_settings) &&
+		    !cache_set_policy(seg, policy_name, policy_settings))
+			return_0;
 
-	if (_linear_type_requested(lp->type_str))
-		return _convert_mirror_linear(cmd, lv, lp);
+		if (policy_settings)
+			dm_config_destroy(policy_settings);
+	} else {
+		const char *discards_name;
 
-	if (segtype_is_raid(lp->segtype))
-		return _convert_mirror_raid(cmd, lv, lp);
+		if (arg_is_set(cmd, zero_ARG))
+			seg->zero_new_blocks = arg_int_value(cmd, zero_ARG, 0);
+		else
+			seg->zero_new_blocks = find_config_tree_bool(cmd, allocation_thin_pool_zero_CFG, vg->profile);
 
-	log_error("Unknown operation on mirror LV %s.", display_lvname(lv));
-	return 0;
-}
+		if (arg_is_set(cmd, discards_ARG))
+			seg->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN);
+		else {
+			if (!(discards_name = find_config_tree_str(cmd, allocation_thin_pool_discards_CFG, vg->profile)))
+				return_0;
+			if (!set_pool_discards(&seg->discards, discards_name))
+				return_0;
+		}
+	}
 
-static int _convert_raid(struct cmd_context *cmd, struct logical_volume *lv,
-			 struct lvconvert_params *lp)
-{
-	if (arg_is_set(cmd, mirrors_ARG))
-		return _convert_raid_number(cmd, lv, lp);
+	/*
+	 * Rename deactivated metadata LV to have _tmeta suffix.
+	 * Implicit checks if metadata_lv is visible.
+	 */
+	if (pool_metadata_name &&
+	    !lv_rename_update(cmd, metadata_lv, meta_name, 0))
+		return_0;
 
-	if (arg_is_set(cmd, splitmirrors_ARG))
-		return _convert_raid_splitmirrors(cmd, lv, lp);
+	if (!attach_pool_metadata_lv(seg, metadata_lv))
+		return_0;
 
-	if (segtype_is_raid(lp->segtype))
-		return _convert_raid_raid(cmd, lv, lp);
+	if (!handle_pool_metadata_spare(vg,
+					metadata_lv->le_count,
+					use_pvh,
+					arg_int_value(cmd, poolmetadataspare_ARG, DEFAULT_POOL_METADATA_SPARE)))
+		return_0;
 
-	if (segtype_is_mirror(lp->segtype))
-		return _convert_raid_mirror(cmd, lv, lp);
+	if (!vg_write(vg) || !vg_commit(vg))
+		return_0;
 
-	if (!strcmp(lp->type_str, SEG_TYPE_NAME_STRIPED))
-		return _convert_raid_striped(cmd, lv, lp);
+	if (seg->zero_new_blocks &&
+	    seg->chunk_size >= DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE * 2)
+		log_warn("WARNING: Pool zeroing and large %s chunk size slows down provisioning.",
+			 display_size(cmd, seg->chunk_size));
 
-	if (_linear_type_requested(lp->type_str))
-		return _convert_raid_linear(cmd, lv, lp);
+	if (activate_pool && !lockd_lv(cmd, pool_lv, "ex", LDLV_PERSISTENT)) {
+		log_error("Failed to lock pool LV %s.", display_lvname(pool_lv));
+		goto out;
+	}
 
-	log_error("Unknown operation on raid LV %s.", display_lvname(lv));
-	return 0;
-}
+	if (activate_pool &&
+	    !activate_lv_excl(cmd, pool_lv)) {
+		log_error("Failed to activate pool logical volume %s.",
+			  display_lvname(pool_lv));
+		/* Deactivate subvolumes */
+		if (!deactivate_lv(cmd, seg_lv(seg, 0)))
+			log_error("Failed to deactivate pool data logical volume %s.",
+				  display_lvname(seg_lv(seg, 0)));
+		if (!deactivate_lv(cmd, seg->metadata_lv))
+			log_error("Failed to deactivate pool metadata logical volume %s.",
+				  display_lvname(seg->metadata_lv));
+		goto out;
+	}
 
-static int _convert_striped(struct cmd_context *cmd, struct logical_volume *lv,
-			    struct lvconvert_params *lp)
-{
-	const char *mirrors_type = find_config_tree_str(cmd, global_mirror_segtype_default_CFG, NULL);
+	r = 1;
 
-	if (!strcmp(lp->type_str, SEG_TYPE_NAME_MIRROR))
-		return _convert_striped_mirror(cmd, lv, lp);
+out:
+	backup(vg);
 
-	if (segtype_is_raid(lp->segtype))
-		return _convert_striped_raid(cmd, lv, lp);
+	if (r)
+		log_print_unless_silent("Converted %s to %s pool.",
+					display_lvname(lv),
+					to_cachepool ? "cache" : "thin");
 
-	/* --mirrors can mean --type mirror or --type raid1 depending on config setting. */
+	/*
+	 * Unlock and free the locks from existing LVs that became pool data
+	 * and meta LVs.
+	 */
+	if (lockd_data_name) {
+		if (!lockd_lv_name(cmd, vg, lockd_data_name, &lockd_data_id, lockd_data_args, "un", LDLV_PERSISTENT))
+			log_error("Failed to unlock pool data LV %s/%s", vg->name, lockd_data_name);
+		lockd_free_lv(cmd, vg, lockd_data_name, &lockd_data_id, lockd_data_args);
+	}
 
-	if (arg_is_set(cmd, mirrors_ARG) && mirrors_type && !strcmp(mirrors_type, SEG_TYPE_NAME_MIRROR))
-		return _convert_striped_mirror(cmd, lv, lp);
+	if (lockd_meta_name) {
+		if (!lockd_lv_name(cmd, vg, lockd_meta_name, &lockd_meta_id, lockd_meta_args, "un", LDLV_PERSISTENT))
+			log_error("Failed to unlock pool metadata LV %s/%s", vg->name, lockd_meta_name);
+		lockd_free_lv(cmd, vg, lockd_meta_name, &lockd_meta_id, lockd_meta_args);
+	}
 
-	if (arg_is_set(cmd, mirrors_ARG) && mirrors_type && !strcmp(mirrors_type, SEG_TYPE_NAME_RAID1))
-		return _convert_striped_raid(cmd, lv, lp);
+	return r;
+#if 0
+revert_new_lv:
+	/* TBD */
+	if (!pool_metadata_lv_name) {
+		if (!deactivate_lv(cmd, metadata_lv)) {
+			log_error("Failed to deactivate metadata lv.");
+			return 0;
+		}
+		if (!lv_remove(metadata_lv) || !vg_write(vg) || !vg_commit(vg))
+			log_error("Manual intervention may be required to remove "
+				  "abandoned LV(s) before retrying.");
+		else
+			backup(vg);
+	}
 
-	log_error("Unknown operation on striped or linear LV %s.", display_lvname(lv));
 	return 0;
+#endif
 }
 
-/*
- * Main entry point.
- * lvconvert performs a specific <operation> on a specific <lv_type>.
- *
- * The <operation> is specified by command line args.
- * The <lv_type> is found using lv_is_foo(lv) functions.
- *
- * for each lvtype,
- *     _convert_lvtype();
- *	 for each arg_is_set(operation)
- *	     _convert_lvtype_operation();
- *
- * FIXME: this code (identifying/routing each unique operation through
- * _convert_lvtype_op) was designed to work based on the new type that
- * the user entered after --type, not the final segment type in lp->type_str.
- * Sometimes the two differ because tricks are played with lp->type_str.
- * So, when the use of arg_type_str(type_ARG) here was replaced with
- * lp->type_str, some commands are no longer identified/routed correctly.
- */
-static int _lvconvert(struct cmd_context *cmd, struct logical_volume *lv,
-		      struct lvconvert_params *lp)
+static int _lvconvert_to_cache_vol(struct cmd_context *cmd,
+			    struct logical_volume *lv,
+			    struct logical_volume *cachepool_lv)
 {
-	struct lv_segment *seg = first_seg(lv);
-	int ret = 0;
+	struct logical_volume *cache_lv;
+	cache_mode_t cache_mode = 0;
+	const char *policy_name = NULL;
+	struct dm_config_tree *policy_settings = NULL;
 
-	/* Set up segtype either from type_str or else to match the existing one. */
-	if (!*lp->type_str)
-		lp->segtype = seg->segtype;
-	else if (!(lp->segtype = get_segtype_from_string(cmd, lp->type_str)))
-		goto_out;
+	if (!validate_lv_cache_create_pool(cachepool_lv))
+		return_0;
 
-	if (!strcmp(lp->type_str, SEG_TYPE_NAME_MIRROR)) {
-		if (!lp->mirrors_supplied && !seg_is_raid1(seg)) {
-			log_error("Conversions to --type mirror require -m/--mirrors");
-			goto out;
-		}
-	}
+	if (!get_cache_params(cmd, &cache_mode, &policy_name, &policy_settings))
+		return_0;
 
-	/* lv->segtype can't be NULL */
-	if (activation() && lp->segtype->ops->target_present &&
-	    !lp->segtype->ops->target_present(cmd, NULL, &lp->target_attr)) {
-		log_error("%s: Required device-mapper target(s) not "
-			  "detected in your kernel.", lp->segtype->name);
-		goto out;
-	}
+	if (!archive(lv->vg))
+		return_0;
 
-	/* Process striping parameters */
-	/* FIXME This is incomplete */
-	if (_mirror_or_raid_type_requested(cmd, lp->type_str) || _raid0_type_requested(lp->type_str) ||
-	    _striped_type_requested(lp->type_str) || lp->mirrorlog || lp->corelog) {
-		/* FIXME Handle +/- adjustments too? */
-		if (!get_stripe_params(cmd, lp->segtype, &lp->stripes, &lp->stripe_size, &lp->stripes_supplied, &lp->stripe_size_supplied))
-			goto_out;
+	if (!(cache_lv = lv_cache_create(cachepool_lv, lv)))
+		return_0;
 
-		if (_raid0_type_requested(lp->type_str) || _striped_type_requested(lp->type_str))
-			/* FIXME Shouldn't need to override get_stripe_params which defaults to 1 stripe (i.e. linear)! */
-			/* The default keeps existing number of stripes, handled inside the library code */
-			if (!arg_is_set(cmd, stripes_long_ARG))
-				lp->stripes = 0;
-	}
+	if (!cache_set_cache_mode(first_seg(cache_lv), cache_mode))
+		return_0;
 
-	if (lv_is_cache(lv))
-		lv = seg_lv(first_seg(lv), 0);
+	if (!cache_set_policy(first_seg(cache_lv), policy_name, policy_settings))
+		return_0;
 
-	if (lv_is_mirror(lv)) {
-		ret = _convert_mirror(cmd, lv, lp);
-		goto out;
-	}
+	if (policy_settings)
+		dm_config_destroy(policy_settings);
 
-	if (lv_is_raid(lv)) {
-		ret = _convert_raid(cmd, lv, lp);
-		goto out;
-	}
+	cache_check_for_warns(first_seg(cache_lv));
 
-	/*
-	 * FIXME: add lv_is_striped() and lv_is_linear()?
-	 * This does not include raid0 which is caught by the test above.
-	 * If operations differ between striped and linear, split this case.
-	 */
-	if (segtype_is_striped(seg->segtype) || segtype_is_linear(seg->segtype)) {
-		ret = _convert_striped(cmd, lv, lp);
-		goto out;
-	}
+	if (!lv_update_and_reload(cache_lv))
+		return_0;
 
-	/*
-	 * The intention is to explicitly check all cases above and never
-	 * reach here, but this covers anything that was missed.
-	 */
-	log_error("Cannot convert LV %s.", display_lvname(lv));
+	log_print_unless_silent("Logical volume %s is now cached.",
+				display_lvname(cache_lv));
 
-out:
-	return ret ? ECMD_PROCESSED : ECMD_FAILED;
+	return 1;
 }
 
 static struct convert_poll_id_list* _convert_poll_id_list_create(struct cmd_context *cmd,
@@ -4990,11 +3589,6 @@ int lvconvert_replace_pv_cmd(struct cmd_context *cmd, int argc, char **argv)
 
 
 /*
- * snapshot-related lvconvert utilities
- */
-
-
-/*
  * Merge a COW snapshot LV into its origin.
  */
 
@@ -5661,7 +4255,7 @@ static int _lvconvert_raid_types_single(struct cmd_context *cmd, struct logical_
 
 	lp->lv_to_poll = lv;
 
-	ret = _lvconvert(cmd, lv, lp);
+	ret = _lvconvert_raid_types(cmd, lv, lp);
 
 	if (ret != ECMD_PROCESSED)
 		return_ECMD_FAILED;
@@ -5805,8 +4399,8 @@ static int _lvconvert_change_mirrorlog_single(struct cmd_context *cmd, struct lo
 
 	lp->pvh = use_pvh;
 
-	/* FIXME: extract the mirrorlog functionality out of _lvconvert()? */
-	return _lvconvert(cmd, lv, lp);
+	/* FIXME: extract the mirrorlog functionality out of _lvconvert_raid_types()? */
+	return _lvconvert_raid_types(cmd, lv, lp);
 }
 
 int lvconvert_change_mirrorlog_cmd(struct cmd_context * cmd, int argc, char **argv)
@@ -5861,8 +4455,8 @@ static int _lvconvert_split_mirror_images_single(struct cmd_context *cmd, struct
 
 	lp->pvh = use_pvh;
 
-	/* FIXME: extract the split functionality out of _lvconvert()? */
-	return _lvconvert(cmd, lv, lp);
+	/* FIXME: extract the split functionality out of _lvconvert_raid_types()? */
+	return _lvconvert_raid_types(cmd, lv, lp);
 }
 
 int lvconvert_split_mirror_images_cmd(struct cmd_context * cmd, int argc, char **argv)




More information about the lvm-devel mailing list