695 lines
29 KiB
Diff
695 lines
29 KiB
Diff
conf/example.conf.in | 7 +++
|
|
device_mapper/all.h | 16 +++++--
|
|
device_mapper/libdm-deptree.c | 39 ++++++++++++-----
|
|
lib/activate/dev_manager.c | 8 ++--
|
|
lib/config/config_settings.h | 5 +++
|
|
lib/config/defaults.h | 2 +
|
|
lib/format_text/flags.c | 1 +
|
|
lib/metadata/lv_manip.c | 31 ++++++++++++++
|
|
lib/metadata/merge.c | 2 +
|
|
lib/metadata/metadata-exported.h | 11 +++++
|
|
lib/metadata/metadata.h | 13 ++++++
|
|
lib/metadata/pool_manip.c | 46 ++++++++++++++++++++
|
|
lib/metadata/thin_manip.c | 92 ++++++++++++++++++++++++++--------------
|
|
lib/thin/thin.c | 22 +++++++---
|
|
man/lvmthin.7_main | 10 ++++-
|
|
tools/lvconvert.c | 4 ++
|
|
tools/lvcreate.c | 2 +
|
|
17 files changed, 256 insertions(+), 55 deletions(-)
|
|
|
|
diff --git a/conf/example.conf.in b/conf/example.conf.in
|
|
index d149ed9..107a071 100644
|
|
--- a/conf/example.conf.in
|
|
+++ b/conf/example.conf.in
|
|
@@ -494,6 +494,13 @@ allocation {
|
|
# This configuration option has an automatic default value.
|
|
# thin_pool_metadata_require_separate_pvs = 0
|
|
|
|
+ # Configuration option allocation/thin_pool_crop_metadata.
|
|
+ # Older version of lvm2 cropped pool's metadata size to 15.81 GiB.
|
|
+ # This is slightly less then the actual maximum 15.88 GiB.
|
|
+ # For compatibility with older version and use of cropped size set to 1.
|
|
+ # This configuration option has an automatic default value.
|
|
+ # thin_pool_crop_metadata = 0
|
|
+
|
|
# Configuration option allocation/thin_pool_zero.
|
|
# Thin pool data chunks are zeroed before they are first used.
|
|
# Zeroing with a larger thin pool chunk size reduces performance.
|
|
diff --git a/device_mapper/all.h b/device_mapper/all.h
|
|
index 1080d25..489ca1c 100644
|
|
--- a/device_mapper/all.h
|
|
+++ b/device_mapper/all.h
|
|
@@ -1072,10 +1072,10 @@ int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
|
|
#define DM_THIN_MIN_DATA_BLOCK_SIZE (UINT32_C(128))
|
|
#define DM_THIN_MAX_DATA_BLOCK_SIZE (UINT32_C(2097152))
|
|
/*
|
|
- * Max supported size for thin pool metadata device (17112760320 bytes)
|
|
- * Limitation is hardcoded into the kernel and bigger device size
|
|
- * is not accepted.
|
|
+ * Max supported size for thin pool metadata device (17045913600 bytes)
|
|
* drivers/md/dm-thin-metadata.h THIN_METADATA_MAX_SECTORS
|
|
+ * But here DM_THIN_MAX_METADATA_SIZE got defined incorrectly
|
|
+ * Correct size is (UINT64_C(255) * ((1 << 14) - 64) * (4096 / (1 << 9)))
|
|
*/
|
|
#define DM_THIN_MAX_METADATA_SIZE (UINT64_C(255) * (1 << 14) * (4096 / (1 << 9)) - 256 * 1024)
|
|
|
|
@@ -1088,6 +1088,16 @@ int dm_tree_node_add_thin_pool_target(struct dm_tree_node *node,
|
|
uint64_t low_water_mark,
|
|
unsigned skip_block_zeroing);
|
|
|
|
+int dm_tree_node_add_thin_pool_target_v1(struct dm_tree_node *node,
|
|
+ uint64_t size,
|
|
+ uint64_t transaction_id,
|
|
+ const char *metadata_uuid,
|
|
+ const char *pool_uuid,
|
|
+ uint32_t data_block_size,
|
|
+ uint64_t low_water_mark,
|
|
+ unsigned skip_block_zeroing,
|
|
+ unsigned crop_metadata);
|
|
+
|
|
/* Supported messages for thin provision target */
|
|
typedef enum {
|
|
DM_THIN_MESSAGE_CREATE_SNAP, /* device_id, origin_id */
|
|
diff --git a/device_mapper/libdm-deptree.c b/device_mapper/libdm-deptree.c
|
|
index 6ce956f..5b60dc9 100644
|
|
--- a/device_mapper/libdm-deptree.c
|
|
+++ b/device_mapper/libdm-deptree.c
|
|
@@ -3979,6 +3979,24 @@ int dm_tree_node_add_thin_pool_target(struct dm_tree_node *node,
|
|
uint64_t low_water_mark,
|
|
unsigned skip_block_zeroing)
|
|
{
|
|
+ return dm_tree_node_add_thin_pool_target_v1(node, size, transaction_id,
|
|
+ metadata_uuid, pool_uuid,
|
|
+ data_block_size,
|
|
+ low_water_mark,
|
|
+ skip_block_zeroing,
|
|
+ 1);
|
|
+}
|
|
+
|
|
+int dm_tree_node_add_thin_pool_target_v1(struct dm_tree_node *node,
|
|
+ uint64_t size,
|
|
+ uint64_t transaction_id,
|
|
+ const char *metadata_uuid,
|
|
+ const char *pool_uuid,
|
|
+ uint32_t data_block_size,
|
|
+ uint64_t low_water_mark,
|
|
+ unsigned skip_block_zeroing,
|
|
+ unsigned crop_metadata)
|
|
+{
|
|
struct load_segment *seg, *mseg;
|
|
uint64_t devsize = 0;
|
|
|
|
@@ -4005,17 +4023,18 @@ int dm_tree_node_add_thin_pool_target(struct dm_tree_node *node,
|
|
if (!_link_tree_nodes(node, seg->metadata))
|
|
return_0;
|
|
|
|
- /* FIXME: more complex target may need more tweaks */
|
|
- dm_list_iterate_items(mseg, &seg->metadata->props.segs) {
|
|
- devsize += mseg->size;
|
|
- if (devsize > DM_THIN_MAX_METADATA_SIZE) {
|
|
- log_debug_activation("Ignoring %" PRIu64 " of device.",
|
|
- devsize - DM_THIN_MAX_METADATA_SIZE);
|
|
- mseg->size -= (devsize - DM_THIN_MAX_METADATA_SIZE);
|
|
- devsize = DM_THIN_MAX_METADATA_SIZE;
|
|
- /* FIXME: drop remaining segs */
|
|
+ if (crop_metadata)
|
|
+ /* FIXME: more complex target may need more tweaks */
|
|
+ dm_list_iterate_items(mseg, &seg->metadata->props.segs) {
|
|
+ devsize += mseg->size;
|
|
+ if (devsize > DM_THIN_MAX_METADATA_SIZE) {
|
|
+ log_debug_activation("Ignoring %" PRIu64 " of device.",
|
|
+ devsize - DM_THIN_MAX_METADATA_SIZE);
|
|
+ mseg->size -= (devsize - DM_THIN_MAX_METADATA_SIZE);
|
|
+ devsize = DM_THIN_MAX_METADATA_SIZE;
|
|
+ /* FIXME: drop remaining segs */
|
|
+ }
|
|
}
|
|
- }
|
|
|
|
if (!(seg->pool = dm_tree_find_node_by_uuid(node->dtree, pool_uuid))) {
|
|
log_error("Missing pool uuid %s.", pool_uuid);
|
|
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
|
|
index 8d27bd3..9a25482 100644
|
|
--- a/lib/activate/dev_manager.c
|
|
+++ b/lib/activate/dev_manager.c
|
|
@@ -261,7 +261,7 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
|
|
int dmtask;
|
|
int with_flush; /* TODO: arg for _info_run */
|
|
void *target = NULL;
|
|
- uint64_t target_start, target_length, start, length;
|
|
+ uint64_t target_start, target_length, start, length, length_crop = 0;
|
|
char *target_name, *target_params;
|
|
const char *devname;
|
|
|
|
@@ -297,7 +297,7 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
|
|
/* Uses max DM_THIN_MAX_METADATA_SIZE sectors for metadata device */
|
|
if (lv_is_thin_pool_metadata(seg_status->seg->lv) &&
|
|
(length > DM_THIN_MAX_METADATA_SIZE))
|
|
- length = DM_THIN_MAX_METADATA_SIZE;
|
|
+ length_crop = DM_THIN_MAX_METADATA_SIZE;
|
|
|
|
/* Uses virtual size with headers for VDO pool device */
|
|
if (lv_is_vdo_pool(seg_status->seg->lv))
|
|
@@ -310,7 +310,9 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
|
|
target = dm_get_next_target(dmt, target, &target_start,
|
|
&target_length, &target_name, &target_params);
|
|
|
|
- if ((start == target_start) && (length == target_length))
|
|
+ if ((start == target_start) &&
|
|
+ ((length == target_length) ||
|
|
+ (length_crop && (length_crop == target_length))))
|
|
break; /* Keep target_params when matching segment is found */
|
|
|
|
target_params = NULL; /* Marking this target_params unusable */
|
|
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
|
|
index 3c4032e..cb4e23a 100644
|
|
--- a/lib/config/config_settings.h
|
|
+++ b/lib/config/config_settings.h
|
|
@@ -628,6 +628,11 @@ cfg(allocation_cache_pool_max_chunks_CFG, "cache_pool_max_chunks", allocation_CF
|
|
cfg(allocation_thin_pool_metadata_require_separate_pvs_CFG, "thin_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 89), NULL, 0, NULL,
|
|
"Thin pool metadata and data will always use different PVs.\n")
|
|
|
|
+cfg(allocation_thin_pool_crop_metadata_CFG, "thin_pool_crop_metadata", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_CROP_METADATA, vsn(2, 3, 12), NULL, 0, NULL,
|
|
+ "Older version of lvm2 cropped pool's metadata size to 15.81 GiB.\n"
|
|
+ "This is slightly less then the actual maximum 15.88 GiB.\n"
|
|
+ "For compatibility with older version and use of cropped size set to 1.\n")
|
|
+
|
|
cfg(allocation_thin_pool_zero_CFG, "thin_pool_zero", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_ZERO, vsn(2, 2, 99), NULL, 0, NULL,
|
|
"Thin pool data chunks are zeroed before they are first used.\n"
|
|
"Zeroing with a larger thin pool chunk size reduces performance.\n")
|
|
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
|
|
index 708a575..bcc20cc 100644
|
|
--- a/lib/config/defaults.h
|
|
+++ b/lib/config/defaults.h
|
|
@@ -118,6 +118,8 @@
|
|
#define DEFAULT_THIN_REPAIR_OPTION1 ""
|
|
#define DEFAULT_THIN_REPAIR_OPTIONS_CONFIG "#S" DEFAULT_THIN_REPAIR_OPTION1
|
|
#define DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS 0
|
|
+#define DEFAULT_THIN_POOL_CROP_METADATA 0
|
|
+#define DEFAULT_THIN_POOL_MAX_METADATA_SIZE_V1_KB (UINT64_C(255) * ((1 << 14) - 64) * 4) /* KB */ /* 0x3f8040 blocks */
|
|
#define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (DM_THIN_MAX_METADATA_SIZE / 2) /* KB */
|
|
#define DEFAULT_THIN_POOL_MIN_METADATA_SIZE 2048 /* KB */
|
|
#define DEFAULT_THIN_POOL_OPTIMAL_METADATA_SIZE (128 * 1024) /* KB */
|
|
diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c
|
|
index bc93a5d..4cee14a 100644
|
|
--- a/lib/format_text/flags.c
|
|
+++ b/lib/format_text/flags.c
|
|
@@ -72,6 +72,7 @@ static const struct flag _lv_flags[] = {
|
|
{LV_ACTIVATION_SKIP, "ACTIVATION_SKIP", COMPATIBLE_FLAG},
|
|
{LV_ERROR_WHEN_FULL, "ERROR_WHEN_FULL", COMPATIBLE_FLAG},
|
|
{LV_METADATA_FORMAT, "METADATA_FORMAT", SEGTYPE_FLAG},
|
|
+ {LV_CROP_METADATA, "CROP_METADATA", SEGTYPE_FLAG},
|
|
{LV_CACHE_VOL, "CACHE_VOL", COMPATIBLE_FLAG},
|
|
{LV_CACHE_USES_CACHEVOL, "CACHE_USES_CACHEVOL", SEGTYPE_FLAG},
|
|
{LV_NOSCAN, NULL, 0},
|
|
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
|
|
index 443d32c..445c4ad 100644
|
|
--- a/lib/metadata/lv_manip.c
|
|
+++ b/lib/metadata/lv_manip.c
|
|
@@ -5384,6 +5384,8 @@ static int _lvresize_adjust_extents(struct logical_volume *lv,
|
|
uint32_t existing_extents;
|
|
uint32_t seg_size = 0;
|
|
uint32_t new_extents;
|
|
+ uint64_t max_metadata_size;
|
|
+ thin_crop_metadata_t crop;
|
|
int reducing = 0;
|
|
|
|
seg_last = last_seg(lv);
|
|
@@ -5544,6 +5546,33 @@ static int _lvresize_adjust_extents(struct logical_volume *lv,
|
|
return 1;
|
|
}
|
|
}
|
|
+ } else if (lv_is_thin_pool_metadata(lv)) {
|
|
+ if (!(seg = get_only_segment_using_this_lv(lv)))
|
|
+ return_0;
|
|
+
|
|
+ max_metadata_size = get_thin_pool_max_metadata_size(cmd, vg->profile, &crop);
|
|
+
|
|
+ if (((uint64_t)lp->extents * vg->extent_size) > max_metadata_size) {
|
|
+ lp->extents = (max_metadata_size + vg->extent_size - 1) / vg->extent_size;
|
|
+ log_print_unless_silent("Reached maximum pool metadata size %s (%" PRIu32 " extents).",
|
|
+ display_size(vg->cmd, max_metadata_size), lp->extents);
|
|
+ }
|
|
+
|
|
+ if (existing_logical_extents >= lp->extents)
|
|
+ lp->extents = existing_logical_extents;
|
|
+
|
|
+ crop = get_thin_pool_crop_metadata(cmd, crop, (uint64_t)lp->extents * vg->extent_size);
|
|
+
|
|
+ if (seg->crop_metadata != crop) {
|
|
+ seg->crop_metadata = crop;
|
|
+ seg->lv->status |= LV_CROP_METADATA;
|
|
+ /* Crop change require reload even if there no size change */
|
|
+ lp->size_changed = 1;
|
|
+ log_print_unless_silent("Thin pool will use metadata without cropping.");
|
|
+ }
|
|
+
|
|
+ if (!(seg_size = lp->extents - existing_logical_extents))
|
|
+ return 1; /* No change in metadata size */
|
|
}
|
|
} else { /* If reducing, find stripes, stripesize & size of last segment */
|
|
if (lp->stripes || lp->stripe_size || lp->mirrors)
|
|
@@ -8388,6 +8417,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
|
first_seg(lv)->chunk_size = lp->chunk_size;
|
|
first_seg(lv)->zero_new_blocks = lp->zero_new_blocks;
|
|
first_seg(lv)->discards = lp->discards;
|
|
+ if ((first_seg(lv)->crop_metadata = lp->crop_metadata) == THIN_CROP_METADATA_NO)
|
|
+ lv->status |= LV_CROP_METADATA;
|
|
if (!recalculate_pool_chunk_size_with_dev_hints(lv, lp->thin_chunk_size_calc_policy)) {
|
|
stack;
|
|
goto revert_new_lv;
|
|
diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c
|
|
index 0aa2293..eff59ae 100644
|
|
--- a/lib/metadata/merge.c
|
|
+++ b/lib/metadata/merge.c
|
|
@@ -495,6 +495,8 @@ static void _check_lv_segment(struct logical_volume *lv, struct lv_segment *seg,
|
|
seg_error("sets discards");
|
|
if (!dm_list_empty(&seg->thin_messages))
|
|
seg_error("sets thin_messages list");
|
|
+ if (seg->lv->status & LV_CROP_METADATA)
|
|
+ seg_error("sets CROP_METADATA flag");
|
|
}
|
|
|
|
if (seg_is_thin_volume(seg)) {
|
|
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
|
|
index 54dc29f..0e57722 100644
|
|
--- a/lib/metadata/metadata-exported.h
|
|
+++ b/lib/metadata/metadata-exported.h
|
|
@@ -143,6 +143,7 @@
|
|
|
|
#define LV_REMOVE_AFTER_RESHAPE UINT64_C(0x0400000000000000) /* LV needs to be removed after a shrinking reshape */
|
|
#define LV_METADATA_FORMAT UINT64_C(0x0800000000000000) /* LV has segments with metadata format */
|
|
+#define LV_CROP_METADATA UINT64_C(0x0000000000000400) /* LV - also VG CLUSTERED */
|
|
|
|
#define LV_RESHAPE UINT64_C(0x1000000000000000) /* Ongoing reshape (number of stripes, stripesize or raid algorithm change):
|
|
used as SEGTYPE_FLAG to prevent activation on old runtime */
|
|
@@ -326,6 +327,12 @@ typedef enum {
|
|
} thin_discards_t;
|
|
|
|
typedef enum {
|
|
+ THIN_CROP_METADATA_UNSELECTED = 0, /* 'auto' selects */
|
|
+ THIN_CROP_METADATA_NO,
|
|
+ THIN_CROP_METADATA_YES,
|
|
+} thin_crop_metadata_t;
|
|
+
|
|
+typedef enum {
|
|
CACHE_MODE_UNSELECTED = 0,
|
|
CACHE_MODE_WRITETHROUGH,
|
|
CACHE_MODE_WRITEBACK,
|
|
@@ -502,6 +509,7 @@ struct lv_segment {
|
|
uint64_t transaction_id; /* For thin_pool, thin */
|
|
thin_zero_t zero_new_blocks; /* For thin_pool */
|
|
thin_discards_t discards; /* For thin_pool */
|
|
+ thin_crop_metadata_t crop_metadata; /* For thin_pool */
|
|
struct dm_list thin_messages; /* For thin_pool */
|
|
struct logical_volume *external_lv; /* For thin */
|
|
struct logical_volume *pool_lv; /* For thin, cache */
|
|
@@ -885,6 +893,8 @@ int update_thin_pool_params(struct cmd_context *cmd,
|
|
unsigned attr,
|
|
uint32_t pool_data_extents,
|
|
uint32_t *pool_metadata_extents,
|
|
+ struct logical_volume *metadata_lv,
|
|
+ unsigned *crop_metadata,
|
|
int *chunk_size_calc_method, uint32_t *chunk_size,
|
|
thin_discards_t *discards, thin_zero_t *zero_new_blocks);
|
|
|
|
@@ -1011,6 +1021,7 @@ struct lvcreate_params {
|
|
|
|
uint64_t permission; /* all */
|
|
unsigned error_when_full; /* when segment supports it */
|
|
+ thin_crop_metadata_t crop_metadata;
|
|
uint32_t read_ahead; /* all */
|
|
int approx_alloc; /* all */
|
|
alloc_policy_t alloc; /* all */
|
|
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
|
|
index 2c22450..0f230e4 100644
|
|
--- a/lib/metadata/metadata.h
|
|
+++ b/lib/metadata/metadata.h
|
|
@@ -512,8 +512,21 @@ int pool_below_threshold(const struct lv_segment *pool_seg);
|
|
int pool_check_overprovisioning(const struct logical_volume *lv);
|
|
int create_pool(struct logical_volume *pool_lv, const struct segment_type *segtype,
|
|
struct alloc_handle *ah, uint32_t stripes, uint32_t stripe_size);
|
|
+uint64_t get_thin_pool_max_metadata_size(struct cmd_context *cmd, struct profile *profile,
|
|
+ thin_crop_metadata_t *crop);
|
|
+thin_crop_metadata_t get_thin_pool_crop_metadata(struct cmd_context *cmd,
|
|
+ thin_crop_metadata_t crop,
|
|
+ uint64_t metadata_size);
|
|
uint64_t estimate_thin_pool_metadata_size(uint32_t data_extents, uint32_t extent_size, uint32_t chunk_size);
|
|
|
|
+int update_pool_metadata_min_max(struct cmd_context *cmd,
|
|
+ uint32_t extent_size,
|
|
+ uint64_t min_metadata_size, /* required min */
|
|
+ uint64_t max_metadata_size, /* writable max */
|
|
+ uint64_t *metadata_size, /* current calculated */
|
|
+ struct logical_volume *metadata_lv, /* name of converted LV or NULL */
|
|
+ uint32_t *metadata_extents); /* resulting extent count */
|
|
+
|
|
/*
|
|
* Begin skeleton for external LVM library
|
|
*/
|
|
diff --git a/lib/metadata/pool_manip.c b/lib/metadata/pool_manip.c
|
|
index a9dc611..b67882e 100644
|
|
--- a/lib/metadata/pool_manip.c
|
|
+++ b/lib/metadata/pool_manip.c
|
|
@@ -742,6 +742,52 @@ int handle_pool_metadata_spare(struct volume_group *vg, uint32_t extents,
|
|
return 1;
|
|
}
|
|
|
|
+int update_pool_metadata_min_max(struct cmd_context *cmd,
|
|
+ uint32_t extent_size,
|
|
+ uint64_t min_metadata_size, /* required min */
|
|
+ uint64_t max_metadata_size, /* writable max */
|
|
+ uint64_t *metadata_size, /* current calculated */
|
|
+ struct logical_volume *metadata_lv, /* name of converted LV or NULL */
|
|
+ uint32_t *metadata_extents) /* resulting extent count */
|
|
+{
|
|
+ max_metadata_size = dm_round_up(max_metadata_size, extent_size);
|
|
+ min_metadata_size = dm_round_up(min_metadata_size, extent_size);
|
|
+
|
|
+ if (*metadata_size > max_metadata_size) {
|
|
+ if (metadata_lv) {
|
|
+ log_print_unless_silent("Size %s of pool metadata volume %s is bigger then maximum usable size %s.",
|
|
+ display_size(cmd, *metadata_size),
|
|
+ display_lvname(metadata_lv),
|
|
+ display_size(cmd, max_metadata_size));
|
|
+ } else {
|
|
+ if (*metadata_extents)
|
|
+ log_print_unless_silent("Reducing pool metadata size %s to maximum usable size %s.",
|
|
+ display_size(cmd, *metadata_size),
|
|
+ display_size(cmd, max_metadata_size));
|
|
+ *metadata_size = max_metadata_size;
|
|
+ }
|
|
+ } else if (*metadata_size < min_metadata_size) {
|
|
+ if (metadata_lv) {
|
|
+ log_error("Can't use volume %s with size %s as pool metadata. Minimal required size is %s.",
|
|
+ display_lvname(metadata_lv),
|
|
+ display_size(cmd, *metadata_size),
|
|
+ display_size(cmd, min_metadata_size));
|
|
+ return 0;
|
|
+ } else {
|
|
+ if (*metadata_extents)
|
|
+ log_print_unless_silent("Extending pool metadata size %s to required minimal size %s.",
|
|
+ display_size(cmd, *metadata_size),
|
|
+ display_size(cmd, min_metadata_size));
|
|
+ *metadata_size = min_metadata_size;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!(*metadata_extents = extents_from_size(cmd, *metadata_size, extent_size)))
|
|
+ return_0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
int vg_set_pool_metadata_spare(struct logical_volume *lv)
|
|
{
|
|
char new_name[NAME_LEN];
|
|
diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c
|
|
index 4591dd7..451c382 100644
|
|
--- a/lib/metadata/thin_manip.c
|
|
+++ b/lib/metadata/thin_manip.c
|
|
@@ -610,9 +610,9 @@ static uint64_t _estimate_metadata_size(uint32_t data_extents, uint32_t extent_s
|
|
}
|
|
|
|
/* Estimate maximal supportable thin pool data size for given chunk_size */
|
|
-static uint64_t _estimate_max_data_size(uint32_t chunk_size)
|
|
+static uint64_t _estimate_max_data_size(uint64_t max_metadata_size, uint32_t chunk_size)
|
|
{
|
|
- return chunk_size * (DEFAULT_THIN_POOL_MAX_METADATA_SIZE * 2) * SECTOR_SIZE / UINT64_C(64);
|
|
+ return max_metadata_size * chunk_size * SECTOR_SIZE / UINT64_C(64);
|
|
}
|
|
|
|
/* Estimate thin pool chunk size from data and metadata size (in sector units) */
|
|
@@ -662,6 +662,38 @@ int get_default_allocation_thin_pool_chunk_size(struct cmd_context *cmd, struct
|
|
return 1;
|
|
}
|
|
|
|
+/* Return max supported metadata size with selected cropping */
|
|
+uint64_t get_thin_pool_max_metadata_size(struct cmd_context *cmd, struct profile *profile,
|
|
+ thin_crop_metadata_t *crop)
|
|
+{
|
|
+ *crop = find_config_tree_bool(cmd, allocation_thin_pool_crop_metadata_CFG, profile) ?
|
|
+ THIN_CROP_METADATA_YES : THIN_CROP_METADATA_NO;
|
|
+
|
|
+ return (*crop == THIN_CROP_METADATA_NO) ?
|
|
+ (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE_V1_KB) : (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * With existing crop method, check if the metadata_size would need cropping.
|
|
+ * If not, set UNSELECTED, otherwise print some verbose info about selected cropping
|
|
+ */
|
|
+thin_crop_metadata_t get_thin_pool_crop_metadata(struct cmd_context *cmd,
|
|
+ thin_crop_metadata_t crop,
|
|
+ uint64_t metadata_size)
|
|
+{
|
|
+ const uint64_t crop_size = (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE);
|
|
+
|
|
+ if (metadata_size > crop_size) {
|
|
+ if (crop == THIN_CROP_METADATA_NO)
|
|
+ log_verbose("Using metadata size without cropping.");
|
|
+ else
|
|
+ log_verbose("Cropping metadata size to %s.", display_size(cmd, crop_size));
|
|
+ } else
|
|
+ crop = THIN_CROP_METADATA_UNSELECTED;
|
|
+
|
|
+ return crop;
|
|
+}
|
|
+
|
|
int update_thin_pool_params(struct cmd_context *cmd,
|
|
struct profile *profile,
|
|
uint32_t extent_size,
|
|
@@ -669,10 +701,13 @@ int update_thin_pool_params(struct cmd_context *cmd,
|
|
unsigned attr,
|
|
uint32_t pool_data_extents,
|
|
uint32_t *pool_metadata_extents,
|
|
+ struct logical_volume *metadata_lv,
|
|
+ thin_crop_metadata_t *crop_metadata,
|
|
int *chunk_size_calc_method, uint32_t *chunk_size,
|
|
thin_discards_t *discards, thin_zero_t *zero_new_blocks)
|
|
{
|
|
- uint64_t pool_metadata_size = (uint64_t) *pool_metadata_extents * extent_size;
|
|
+ uint64_t pool_metadata_size;
|
|
+ uint64_t max_metadata_size;
|
|
uint32_t estimate_chunk_size;
|
|
uint64_t max_pool_data_size;
|
|
const char *str;
|
|
@@ -702,7 +737,9 @@ int update_thin_pool_params(struct cmd_context *cmd,
|
|
*zero_new_blocks = find_config_tree_bool(cmd, allocation_thin_pool_zero_CFG, profile)
|
|
? THIN_ZERO_YES : THIN_ZERO_NO;
|
|
|
|
- if (!pool_metadata_size) {
|
|
+ max_metadata_size = get_thin_pool_max_metadata_size(cmd, profile, crop_metadata);
|
|
+
|
|
+ if (!*pool_metadata_extents) {
|
|
if (!*chunk_size) {
|
|
if (!get_default_allocation_thin_pool_chunk_size(cmd, profile,
|
|
chunk_size,
|
|
@@ -723,20 +760,20 @@ int update_thin_pool_params(struct cmd_context *cmd,
|
|
} else {
|
|
pool_metadata_size = _estimate_metadata_size(pool_data_extents, extent_size, *chunk_size);
|
|
|
|
- if (pool_metadata_size > (DEFAULT_THIN_POOL_MAX_METADATA_SIZE * 2)) {
|
|
+ if (pool_metadata_size > max_metadata_size) {
|
|
/* Suggest bigger chunk size */
|
|
estimate_chunk_size =
|
|
_estimate_chunk_size(pool_data_extents, extent_size,
|
|
- (DEFAULT_THIN_POOL_MAX_METADATA_SIZE * 2), attr);
|
|
+ max_metadata_size, attr);
|
|
log_warn("WARNING: Chunk size is too small for pool, suggested minimum is %s.",
|
|
display_size(cmd, estimate_chunk_size));
|
|
}
|
|
}
|
|
|
|
/* Round up to extent size silently */
|
|
- if (pool_metadata_size % extent_size)
|
|
- pool_metadata_size += extent_size - pool_metadata_size % extent_size;
|
|
+ pool_metadata_size = dm_round_up(pool_metadata_size, extent_size);
|
|
} else {
|
|
+ pool_metadata_size = (uint64_t) *pool_metadata_extents * extent_size;
|
|
estimate_chunk_size = _estimate_chunk_size(pool_data_extents, extent_size,
|
|
pool_metadata_size, attr);
|
|
|
|
@@ -751,7 +788,19 @@ int update_thin_pool_params(struct cmd_context *cmd,
|
|
}
|
|
}
|
|
|
|
- max_pool_data_size = _estimate_max_data_size(*chunk_size);
|
|
+ /* Use not rounded max for data size */
|
|
+ max_pool_data_size = _estimate_max_data_size(max_metadata_size, *chunk_size);
|
|
+
|
|
+ if (!update_pool_metadata_min_max(cmd, extent_size,
|
|
+ 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE,
|
|
+ max_metadata_size,
|
|
+ &pool_metadata_size,
|
|
+ metadata_lv,
|
|
+ pool_metadata_extents))
|
|
+ return_0;
|
|
+
|
|
+ *crop_metadata = get_thin_pool_crop_metadata(cmd, *crop_metadata, pool_metadata_size);
|
|
+
|
|
if ((max_pool_data_size / extent_size) < pool_data_extents) {
|
|
log_error("Selected chunk size %s cannot address more then %s of thin pool data space.",
|
|
display_size(cmd, *chunk_size), display_size(cmd, max_pool_data_size));
|
|
@@ -764,22 +813,6 @@ int update_thin_pool_params(struct cmd_context *cmd,
|
|
if (!validate_thin_pool_chunk_size(cmd, *chunk_size))
|
|
return_0;
|
|
|
|
- if (pool_metadata_size > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) {
|
|
- pool_metadata_size = 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE;
|
|
- if (*pool_metadata_extents)
|
|
- log_warn("WARNING: Maximum supported pool metadata size is %s.",
|
|
- display_size(cmd, pool_metadata_size));
|
|
- } else if (pool_metadata_size < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)) {
|
|
- pool_metadata_size = 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE;
|
|
- if (*pool_metadata_extents)
|
|
- log_warn("WARNING: Minimum supported pool metadata size is %s.",
|
|
- display_size(cmd, pool_metadata_size));
|
|
- }
|
|
-
|
|
- if (!(*pool_metadata_extents =
|
|
- extents_from_size(cmd, pool_metadata_size, extent_size)))
|
|
- return_0;
|
|
-
|
|
if ((uint64_t) *chunk_size > (uint64_t) pool_data_extents * extent_size) {
|
|
log_error("Size of %s data volume cannot be smaller than chunk size %s.",
|
|
segtype->name, display_size(cmd, *chunk_size));
|
|
@@ -958,12 +991,5 @@ int validate_thin_pool_chunk_size(struct cmd_context *cmd, uint32_t chunk_size)
|
|
|
|
uint64_t estimate_thin_pool_metadata_size(uint32_t data_extents, uint32_t extent_size, uint32_t chunk_size)
|
|
{
|
|
- uint64_t sz = _estimate_metadata_size(data_extents, extent_size, chunk_size);
|
|
-
|
|
- if (sz > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE))
|
|
- sz = 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE;
|
|
- else if (sz < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE))
|
|
- sz = 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE;
|
|
-
|
|
- return sz;
|
|
+ return _estimate_metadata_size(data_extents, extent_size, chunk_size);
|
|
}
|
|
diff --git a/lib/thin/thin.c b/lib/thin/thin.c
|
|
index ba0da71..51bc269 100644
|
|
--- a/lib/thin/thin.c
|
|
+++ b/lib/thin/thin.c
|
|
@@ -86,6 +86,7 @@ static int _thin_pool_text_import(struct lv_segment *seg,
|
|
struct logical_volume *pool_data_lv, *pool_metadata_lv;
|
|
const char *discards_str = NULL;
|
|
uint32_t zero = 0;
|
|
+ uint32_t crop = 0;
|
|
|
|
if (!dm_config_get_str(sn, "metadata", &lv_name))
|
|
return SEG_LOG_ERROR("Metadata must be a string in");
|
|
@@ -131,6 +132,13 @@ static int _thin_pool_text_import(struct lv_segment *seg,
|
|
|
|
seg->zero_new_blocks = (zero) ? THIN_ZERO_YES : THIN_ZERO_NO;
|
|
|
|
+ if (dm_config_has_node(sn, "crop_metadata")) {
|
|
+ if (!dm_config_get_uint32(sn, "crop_metadata", &crop))
|
|
+ return SEG_LOG_ERROR("Could not read crop_metadata for");
|
|
+ seg->crop_metadata = (crop) ? THIN_CROP_METADATA_YES : THIN_CROP_METADATA_NO;
|
|
+ seg->lv->status |= LV_CROP_METADATA;
|
|
+ }
|
|
+
|
|
/* Read messages */
|
|
for (; sn; sn = sn->sib)
|
|
if (!(sn->v) && !_thin_pool_add_message(seg, sn->key, sn->child))
|
|
@@ -177,6 +185,9 @@ static int _thin_pool_text_export(const struct lv_segment *seg, struct formatter
|
|
return 0;
|
|
}
|
|
|
|
+ if (seg->crop_metadata != THIN_CROP_METADATA_UNSELECTED)
|
|
+ outf(f, "crop_metadata = %u", (seg->crop_metadata == THIN_CROP_METADATA_YES) ? 1 : 0);
|
|
+
|
|
dm_list_iterate_items(tmsg, &seg->thin_messages) {
|
|
/* Extra validation */
|
|
switch (tmsg->type) {
|
|
@@ -307,11 +318,12 @@ static int _thin_pool_add_target_line(struct dev_manager *dm,
|
|
else
|
|
low_water_mark = 0;
|
|
|
|
- if (!dm_tree_node_add_thin_pool_target(node, len,
|
|
- seg->transaction_id,
|
|
- metadata_dlid, pool_dlid,
|
|
- seg->chunk_size, low_water_mark,
|
|
- (seg->zero_new_blocks == THIN_ZERO_YES) ? 0 : 1))
|
|
+ if (!dm_tree_node_add_thin_pool_target_v1(node, len,
|
|
+ seg->transaction_id,
|
|
+ metadata_dlid, pool_dlid,
|
|
+ seg->chunk_size, low_water_mark,
|
|
+ (seg->zero_new_blocks == THIN_ZERO_YES) ? 0 : 1,
|
|
+ (seg->crop_metadata == THIN_CROP_METADATA_YES) ? 1 : 0))
|
|
return_0;
|
|
|
|
if (attr & THIN_FEATURE_DISCARDS) {
|
|
diff --git a/man/lvmthin.7_main b/man/lvmthin.7_main
|
|
index e6f1d63..3ce34a5 100644
|
|
--- a/man/lvmthin.7_main
|
|
+++ b/man/lvmthin.7_main
|
|
@@ -1104,7 +1104,7 @@ The default value is shown by:
|
|
The amount of thin metadata depends on how many blocks are shared between
|
|
thin LVs (i.e. through snapshots). A thin pool with many snapshots may
|
|
need a larger metadata LV. Thin pool metadata LV sizes can be from 2MiB
|
|
-to 16GiB.
|
|
+to approximately 16GiB.
|
|
|
|
When using lvcreate to create what will become a thin metadata LV, the
|
|
size is specified with the -L|--size option.
|
|
@@ -1119,6 +1119,14 @@ needed, so it is recommended to start with a size of 1GiB which should be
|
|
enough for all practical purposes. A thin pool metadata LV can later be
|
|
manually or automatically extended if needed.
|
|
|
|
+Configurable setting
|
|
+.BR lvm.conf (5)
|
|
+.BR allocation / thin_pool_crop_metadata
|
|
+gives control over cropping to 15.81GiB to stay backward compatible with older
|
|
+versions of lvm2. With enabled cropping there can be observed some problems when
|
|
+using volumes above this size with thin tools (i.e. thin_repair).
|
|
+Cropping should be enabled only when compatibility is required.
|
|
+
|
|
|
|
.SS Create a thin snapshot of an external, read only LV
|
|
|
|
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
|
|
index 7b74afb..ce90279 100644
|
|
--- a/tools/lvconvert.c
|
|
+++ b/tools/lvconvert.c
|
|
@@ -3032,6 +3032,7 @@ static int _lvconvert_to_pool(struct cmd_context *cmd,
|
|
const char *policy_name;
|
|
struct dm_config_tree *policy_settings = NULL;
|
|
int pool_metadata_spare;
|
|
+ thin_crop_metadata_t crop_metadata;
|
|
thin_discards_t discards;
|
|
thin_zero_t zero_new_blocks;
|
|
int r = 0;
|
|
@@ -3196,6 +3197,8 @@ static int _lvconvert_to_pool(struct cmd_context *cmd,
|
|
pool_segtype, target_attr,
|
|
lv->le_count,
|
|
&meta_extents,
|
|
+ metadata_lv,
|
|
+ &crop_metadata,
|
|
&chunk_calc,
|
|
&chunk_size,
|
|
&discards, &zero_new_blocks))
|
|
@@ -3401,6 +3404,7 @@ static int _lvconvert_to_pool(struct cmd_context *cmd,
|
|
goto_bad;
|
|
} else {
|
|
seg->transaction_id = 0;
|
|
+ seg->crop_metadata = crop_metadata;
|
|
seg->chunk_size = chunk_size;
|
|
seg->discards = discards;
|
|
seg->zero_new_blocks = zero_new_blocks;
|
|
diff --git a/tools/lvcreate.c b/tools/lvcreate.c
|
|
index e384291..1ee9e14 100644
|
|
--- a/tools/lvcreate.c
|
|
+++ b/tools/lvcreate.c
|
|
@@ -391,6 +391,8 @@ static int _update_extents_params(struct volume_group *vg,
|
|
lp->segtype, lp->target_attr,
|
|
lp->extents,
|
|
&lp->pool_metadata_extents,
|
|
+ NULL,
|
|
+ &lp->crop_metadata,
|
|
&lp->thin_chunk_size_calc_policy,
|
|
&lp->chunk_size,
|
|
&lp->discards,
|