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,