From 4d2b1a06603d09e815af5bf8687972a000683841 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sun, 26 Feb 2017 20:18:37 +0100
Subject: [PATCH] cache: enable usage of --cachemetadataformat

lvcreate and lvconvert may select cache metadata format when caching LV.
By default lvm2 picks best available format.
---
 WHATS_NEW                        |  1 +
 lib/metadata/cache_manip.c       | 96 ++++++++++++++++++++++++++++++++
 lib/metadata/lv_manip.c          |  2 +
 lib/metadata/metadata-exported.h |  2 +
 tools/lvchange.c                 |  3 +-
 tools/lvconvert.c                | 10 ++--
 tools/lvcreate.c                 |  2 +
 tools/toollib.c                  |  4 ++
 tools/toollib.h                  |  1 +
 9 files changed, 116 insertions(+), 5 deletions(-)

diff --git a/WHATS_NEW b/WHATS_NEW
index 4a5b0f6ed..1cc3e133d 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
 Version 2.02.169 - 
 =====================================
+  Add option for lvcreate/lvconvert --cachemetadataformat auto|1|2.
   Support cache segment with configurable metadata format.
   Add allocation/cache_metadata_format profilable setttings.
   Use function cache_set_params() for both lvcreate and lvconvert.
diff --git a/lib/metadata/cache_manip.c b/lib/metadata/cache_manip.c
index a3e4d957d..e7da89965 100644
--- a/lib/metadata/cache_manip.c
+++ b/lib/metadata/cache_manip.c
@@ -675,6 +675,26 @@ static const char *_get_default_cache_policy(struct cmd_context *cmd)
 	return def;
 }
 
+/* Autodetect best available cache metadata format for a user */
+static cache_metadata_format_t _get_default_cache_metadata_format(struct cmd_context *cmd)
+{
+	const struct segment_type *segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_CACHE);
+	unsigned attr;
+	cache_metadata_format_t f;
+
+	if (!segtype ||
+	    !segtype->ops->target_present ||
+	    !segtype->ops->target_present(cmd, NULL, &attr)) {
+		f = CACHE_METADATA_FORMAT_1;
+		log_warn("WARNING: Cannot detect default cache metadata format, using format: %u.", f);
+	} else {
+		f = (attr & CACHE_FEATURE_METADATA2) ? CACHE_METADATA_FORMAT_2 : CACHE_METADATA_FORMAT_1;
+		log_debug_metadata("Detected default cache metadata format: %u.", f);
+	}
+
+	return f;
+}
+
 int cache_set_policy(struct lv_segment *seg, const char *name,
 		     const struct dm_config_tree *settings)
 {
@@ -773,12 +793,85 @@ out:
 	return r;
 }
 
+/*
+ * Sets metadata format on cache pool segment with these rules:
+ * 1. When 'cache-pool' segment is passed, sets only for selected formats (1 or 2).
+ * 2. For 'cache' segment passed in we know cache pool segment.
+ *      When passed format is 0 (UNSELECTED) with 'cache' segment - it's the moment
+ *      lvm2 has to figure out 'default' metadata format (1 or 2) from
+ *      configuration or profiles.
+ * 3. If still unselected or selected format is != 1, figure the best supported format
+ *    and either use it or validate users settings is possible.
+ *
+ * Reasoning: A user may create cache-pool and may or may not specify CMFormat.
+ * If the CMFormat has been selected (1 or 2) store this in metadata, otherwise
+ * for an unused cache-pool UNSELECTED CMFormat is used. When caching LV, CMFormat
+ * must be decided and from this moment it's always stored. To support backward
+ * compatibility 'CMFormat 1' is used when it is NOT specified for a cached LV in
+ * lvm2 metadata (no metadata_format=#F element in cache-pool segment).
+ */
+int cache_set_metadata_format(struct lv_segment *seg, cache_metadata_format_t format)
+{
+	cache_metadata_format_t best;
+	struct profile *profile = seg->lv->profile;
+
+	if (seg_is_cache(seg))
+		seg = first_seg(seg->pool_lv);
+	else if (seg_is_cache_pool(seg)) {
+		if (format == CACHE_METADATA_FORMAT_UNSELECTED)
+			return 1; /* Format can be selected later when caching LV */
+	} else {
+		log_error(INTERNAL_ERROR "Cannot set cache metadata format for non cache volume %s.",
+			  display_lvname(seg->lv));
+		return 0;
+	}
+
+	/* Check if we need to search for configured cache metadata format */
+	if (format == CACHE_METADATA_FORMAT_UNSELECTED) {
+		if (seg->cache_metadata_format != CACHE_METADATA_FORMAT_UNSELECTED)
+			return 1; /* Format already selected in cache pool */
+
+		/* Check configurations and profiles */
+		format = find_config_tree_int(seg->lv->vg->cmd, allocation_cache_metadata_format_CFG,
+					      profile);
+	}
+
+	/* See what is a 'best' available cache metadata format
+	 * when the specifed format is other then always existing CMFormat 1 */
+	if (format != CACHE_METADATA_FORMAT_1) {
+		best = _get_default_cache_metadata_format(seg->lv->vg->cmd);
+
+		/* Format was not selected, so use best present on a system */
+		if (format == CACHE_METADATA_FORMAT_UNSELECTED)
+			format = best;
+		else if (format != best) {
+			/* Format is not valid (Only Format 1 or 2 is supported ATM) */
+			log_error("Cache metadata format %u is not supported by kernel target.", format);
+			return 0;
+		}
+	}
+
+	switch (format) {
+	case CACHE_METADATA_FORMAT_2: seg->lv->status |= LV_METADATA_FORMAT; break;
+	case CACHE_METADATA_FORMAT_1: seg->lv->status &= ~LV_METADATA_FORMAT; break;
+	default:
+		log_error(INTERNAL_ERROR "Invalid cache metadata format %u for cache volume %s.",
+			  format, display_lvname(seg->lv));
+		return 0;
+	}
+
+	seg->cache_metadata_format = format;
+
+	return 1;
+}
+
 /*
  * Universal 'wrapper' function  do-it-all
  * to update all commonly specified cache parameters
  */
 int cache_set_params(struct lv_segment *seg,
 		     uint32_t chunk_size,
+		     cache_metadata_format_t format,
 		     cache_mode_t mode,
 		     const char *policy_name,
 		     const struct dm_config_tree *policy_settings)
@@ -786,6 +879,9 @@ int cache_set_params(struct lv_segment *seg,
 	struct lv_segment *pool_seg;
 	struct cmd_context *cmd = seg->lv->vg->cmd;
 
+	if (!cache_set_metadata_format(seg, format))
+		return_0;
+
 	if (!cache_set_cache_mode(seg, mode))
 		return_0;
 
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 506878829..a249d2959 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -7664,6 +7664,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
 	if (lv_is_cache_pool(lv)) {
 		if (!cache_set_params(first_seg(lv),
 				      lp->chunk_size,
+				      lp->cache_metadata_format,
 				      lp->cache_mode,
 				      lp->policy_name,
 				      lp->policy_settings)) {
@@ -7872,6 +7873,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
 
 		if (!cache_set_params(first_seg(lv),
 				      lp->chunk_size,
+				      lp->cache_metadata_format,
 				      lp->cache_mode,
 				      lp->policy_name,
 				      lp->policy_settings))
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index c11899dac..3e6da6331 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -1267,10 +1267,12 @@ const char *display_cache_mode(const struct lv_segment *seg);
 const char *get_cache_mode_name(const struct lv_segment *cache_seg);
 int set_cache_mode(cache_mode_t *mode, const char *cache_mode);
 int cache_set_cache_mode(struct lv_segment *cache_seg, cache_mode_t mode);
+int cache_set_metadata_format(struct lv_segment *cache_seg, cache_metadata_format_t format);
 int cache_set_policy(struct lv_segment *cache_seg, const char *name,
 		     const struct dm_config_tree *settings);
 int cache_set_params(struct lv_segment *seg,
 		     uint32_t chunk_size,
+		     cache_metadata_format_t format,
 		     cache_mode_t mode,
 		     const char *policy_name,
 		     const struct dm_config_tree *policy_settings);
diff --git a/tools/lvchange.c b/tools/lvchange.c
index 3804041f3..abb557a2c 100644
--- a/tools/lvchange.c
+++ b/tools/lvchange.c
@@ -622,6 +622,7 @@ static int _lvchange_persistent(struct cmd_context *cmd,
 
 static int _lvchange_cache(struct cmd_context *cmd, struct logical_volume *lv)
 {
+	cache_metadata_format_t format;
 	cache_mode_t mode;
 	const char *name;
 	struct dm_config_tree *settings = NULL;
@@ -632,7 +633,7 @@ static int _lvchange_cache(struct cmd_context *cmd, struct logical_volume *lv)
 	if (lv_is_cache(lv))
 		pool_seg = first_seg(pool_seg->pool_lv);
 
-	if (!get_cache_params(cmd, &chunk_size, &mode, &name, &settings))
+	if (!get_cache_params(cmd, &chunk_size, &format, &mode, &name, &settings))
 		goto_out;
 
 	if ((mode != CACHE_MODE_UNSELECTED) &&
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 6950c0777..41d32df5b 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -2679,6 +2679,7 @@ static int _lvconvert_to_pool(struct cmd_context *cmd,
 	uint32_t meta_extents;
 	uint32_t chunk_size;
 	int chunk_calc;
+	cache_metadata_format_t cache_metadata_format;
 	cache_mode_t cache_mode;
 	const char *policy_name;
 	struct dm_config_tree *policy_settings = NULL;
@@ -2795,7 +2796,7 @@ static int _lvconvert_to_pool(struct cmd_context *cmd,
 		goto_bad;
 
 	if (to_cachepool &&
-	    !get_cache_params(cmd, &chunk_size, &cache_mode, &policy_name, &policy_settings))
+	    !get_cache_params(cmd, &chunk_size, &cache_metadata_format, &cache_mode, &policy_name, &policy_settings))
 		goto_bad;
 
 	if (metadata_lv)
@@ -3020,7 +3021,7 @@ static int _lvconvert_to_pool(struct cmd_context *cmd,
 
 	/* Apply settings to the new pool seg */
 	if (to_cachepool) {
-		if (!cache_set_params(seg, chunk_size, cache_mode, policy_name, policy_settings))
+		if (!cache_set_params(seg, chunk_size, cache_metadata_format, cache_mode, policy_name, policy_settings))
 			goto_bad;
 	} else {
 		seg->transaction_id = 0;
@@ -3127,6 +3128,7 @@ static int _lvconvert_to_cache_vol(struct cmd_context *cmd,
 {
 	struct logical_volume *cache_lv;
 	uint32_t chunk_size = 0;
+	cache_metadata_format_t cache_metadata_format;
 	cache_mode_t cache_mode;
 	const char *policy_name;
 	struct dm_config_tree *policy_settings = NULL;
@@ -3135,7 +3137,7 @@ static int _lvconvert_to_cache_vol(struct cmd_context *cmd,
 	if (!validate_lv_cache_create_pool(cachepool_lv))
 		return_0;
 
-	if (!get_cache_params(cmd, &chunk_size, &cache_mode, &policy_name, &policy_settings))
+	if (!get_cache_params(cmd, &chunk_size, &cache_metadata_format, &cache_mode, &policy_name, &policy_settings))
 		goto_bad;
 
 	if (!archive(lv->vg))
@@ -3144,7 +3146,7 @@ static int _lvconvert_to_cache_vol(struct cmd_context *cmd,
 	if (!(cache_lv = lv_cache_create(cachepool_lv, lv)))
 		goto_bad;
 
-	if (!cache_set_params(first_seg(cache_lv), chunk_size, cache_mode, policy_name, policy_settings))
+	if (!cache_set_params(first_seg(cache_lv), chunk_size, cache_metadata_format, cache_mode, policy_name, policy_settings))
 		goto_bad;
 
 	if (!lv_update_and_reload(cache_lv))
diff --git a/tools/lvcreate.c b/tools/lvcreate.c
index 52f01a7d1..f77529078 100644
--- a/tools/lvcreate.c
+++ b/tools/lvcreate.c
@@ -645,6 +645,7 @@ static int _read_cache_params(struct cmd_context *cmd,
 
 	if (!get_cache_params(cmd,
 			      &lp->chunk_size,
+			      &lp->cache_metadata_format,
 			      &lp->cache_mode,
 			      &lp->policy_name,
 			      &lp->policy_settings))
@@ -787,6 +788,7 @@ static int _lvcreate_params(struct cmd_context *cmd,
 	type_ARG
 
 #define CACHE_POOL_ARGS \
+	cachemetadataformat_ARG,\
 	cachemode_ARG,\
 	cachepool_ARG,\
 	cachepolicy_ARG,\
diff --git a/tools/toollib.c b/tools/toollib.c
index 8bb9c0a64..1d7b01df7 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1343,6 +1343,7 @@ static int _validate_cachepool_params(const char *name,
 
 int get_cache_params(struct cmd_context *cmd,
 		     uint32_t *chunk_size,
+		     cache_metadata_format_t *cache_metadata_format,
 		     cache_mode_t *cache_mode,
 		     const char **name,
 		     struct dm_config_tree **settings)
@@ -1363,6 +1364,9 @@ int get_cache_params(struct cmd_context *cmd,
 				 display_size(cmd, *chunk_size));
 	}
 
+	*cache_metadata_format = (cache_metadata_format_t)
+		arg_uint_value(cmd, cachemetadataformat_ARG, CACHE_METADATA_FORMAT_UNSELECTED);
+
 	*cache_mode = (cache_mode_t) arg_uint_value(cmd, cachemode_ARG, CACHE_MODE_UNSELECTED);
 
 	*name = arg_str_value(cmd, cachepolicy_ARG, NULL);
diff --git a/tools/toollib.h b/tools/toollib.h
index d5fc0b686..0c9404c89 100644
--- a/tools/toollib.h
+++ b/tools/toollib.h
@@ -221,6 +221,7 @@ int get_stripe_params(struct cmd_context *cmd, const struct segment_type *segtyp
 
 int get_cache_params(struct cmd_context *cmd,
 		     uint32_t *chunk_size,
+		     cache_metadata_format_t *format,
 		     cache_mode_t *cache_mode,
 		     const char **name,
 		     struct dm_config_tree **settings);