mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
allocation: misc fixes for percent/raid rounding
Several fixes for the recent changes that treat allocation percentages as upper limits. Improve messages to make it easier to see what is happening. Fix some cases that failed with errors when they didn't need to. Fix crashes when first_seg() returns NULL. Remove a couple of log_errors that were actually debugging messages.
This commit is contained in:
parent
294671fa62
commit
13c3f53f55
@ -182,6 +182,13 @@ alloc_policy_t get_alloc_from_string(const char *str)
|
||||
return ALLOC_INVALID;
|
||||
}
|
||||
|
||||
static const char *_percent_types[7] = { "NONE", "VGS", "FREE", "LVS", "PVS", "ORIGIN" };
|
||||
|
||||
const char *get_percent_string(percent_type_t def)
|
||||
{
|
||||
return _percent_types[def];
|
||||
}
|
||||
|
||||
#define BASE_UNKNOWN 0
|
||||
#define BASE_SHARED 1
|
||||
#define BASE_1024 8
|
||||
|
@ -64,6 +64,8 @@ const char *get_alloc_string(alloc_policy_t alloc);
|
||||
char alloc_policy_char(alloc_policy_t alloc);
|
||||
alloc_policy_t get_alloc_from_string(const char *str);
|
||||
|
||||
const char *get_percent_string(percent_type_t def);
|
||||
|
||||
char yes_no_prompt(const char *prompt, ...) __attribute__ ((format(printf, 1, 2)));
|
||||
|
||||
#endif
|
||||
|
@ -393,9 +393,13 @@ uint64_t lv_size(const struct logical_volume *lv)
|
||||
static int _lv_mimage_in_sync(const struct logical_volume *lv)
|
||||
{
|
||||
percent_t percent;
|
||||
struct lv_segment *mirror_seg = find_mirror_seg(first_seg(lv));
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
struct lv_segment *mirror_seg;
|
||||
|
||||
if (!(lv->status & MIRROR_IMAGE) || !mirror_seg)
|
||||
if (seg)
|
||||
mirror_seg = find_mirror_seg(seg);
|
||||
|
||||
if (!(lv->status & MIRROR_IMAGE) || !seg || !mirror_seg)
|
||||
return_0;
|
||||
|
||||
if (!lv_mirror_percent(lv->vg->cmd, mirror_seg->lv, 0, &percent,
|
||||
@ -410,7 +414,7 @@ static int _lv_raid_image_in_sync(const struct logical_volume *lv)
|
||||
unsigned s;
|
||||
percent_t percent;
|
||||
char *raid_health;
|
||||
struct lv_segment *raid_seg;
|
||||
struct lv_segment *seg, *raid_seg = NULL;
|
||||
|
||||
/*
|
||||
* If the LV is not active locally,
|
||||
@ -424,7 +428,8 @@ static int _lv_raid_image_in_sync(const struct logical_volume *lv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
raid_seg = get_only_segment_using_this_lv(first_seg(lv)->lv);
|
||||
if ((seg = first_seg(lv)))
|
||||
raid_seg = get_only_segment_using_this_lv(seg->lv);
|
||||
if (!raid_seg) {
|
||||
log_error("Failed to find RAID segment for %s", lv->name);
|
||||
return 0;
|
||||
@ -472,7 +477,7 @@ static int _lv_raid_healthy(const struct logical_volume *lv)
|
||||
{
|
||||
unsigned s;
|
||||
char *raid_health;
|
||||
struct lv_segment *raid_seg;
|
||||
struct lv_segment *seg, *raid_seg = NULL;
|
||||
|
||||
/*
|
||||
* If the LV is not active locally,
|
||||
@ -488,8 +493,8 @@ static int _lv_raid_healthy(const struct logical_volume *lv)
|
||||
|
||||
if (lv->status & RAID)
|
||||
raid_seg = first_seg(lv);
|
||||
else
|
||||
raid_seg = get_only_segment_using_this_lv(first_seg(lv)->lv);
|
||||
else if ((seg = first_seg(lv)))
|
||||
raid_seg = get_only_segment_using_this_lv(seg->lv);
|
||||
|
||||
if (!raid_seg) {
|
||||
log_error("Failed to find RAID segment for %s", lv->name);
|
||||
|
@ -346,6 +346,11 @@ struct lv_segment *get_only_segment_using_this_lv(struct logical_volume *lv)
|
||||
{
|
||||
struct seg_list *sl;
|
||||
|
||||
if (!lv) {
|
||||
log_error(INTERNAL_ERROR "get_only_segment_using_this_lv() called with NULL LV.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dm_list_size(&lv->segs_using_this_lv) != 1) {
|
||||
log_error("%s is expected to have only one segment using it, "
|
||||
"while it has %d", lv->name,
|
||||
@ -943,6 +948,7 @@ struct alloc_handle {
|
||||
* and data device allocated together.
|
||||
*/
|
||||
unsigned alloc_and_split_meta;
|
||||
unsigned split_metadata_is_allocated; /* Metadata has been allocated */
|
||||
|
||||
const struct dm_config_node *cling_tag_list_cn;
|
||||
|
||||
@ -1149,11 +1155,17 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
|
||||
* We need 'log_len' extents for each
|
||||
* RAID device's metadata_area
|
||||
*/
|
||||
if (!approx_alloc)
|
||||
ah->new_extents += (ah->log_len * ah->area_multiple);
|
||||
} else {
|
||||
ah->log_area_count = 0;
|
||||
ah->log_len = 0;
|
||||
}
|
||||
if (approx_alloc) {
|
||||
ah->new_extents = ah->new_extents * ah->area_multiple / (ah->area_count + ah->parity_count);
|
||||
ah->new_extents = (ah->new_extents / ah->area_multiple) * ah->area_multiple;
|
||||
log_debug("Adjusted allocation request to %" PRIu32 " data extents.", ah->new_extents);
|
||||
}
|
||||
} else if (segtype_is_thin_pool(segtype)) {
|
||||
/*
|
||||
* thin_pool uses ah->region_size to
|
||||
@ -1177,6 +1189,7 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
|
||||
find_config_tree_bool(cmd, allocation_cache_pool_metadata_require_separate_pvs_CFG, NULL);
|
||||
if (!ah->mirror_logs_separate) {
|
||||
ah->alloc_and_split_meta = 1;
|
||||
if (!approx_alloc)
|
||||
ah->new_extents += ah->log_len;
|
||||
}
|
||||
} else {
|
||||
@ -1184,6 +1197,9 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
|
||||
ah->log_len = !metadata_area_count ? 0 :
|
||||
mirror_log_extents(ah->region_size, extent_size,
|
||||
new_extents / ah->area_multiple);
|
||||
ah->new_extents = ah->new_extents * ah->area_multiple / ah->area_count;
|
||||
ah->new_extents = (ah->new_extents / ah->area_multiple) * ah->area_multiple;
|
||||
log_debug("Adjusted allocation request to %" PRIu32 " data extents.", ah->new_extents);
|
||||
}
|
||||
|
||||
for (s = 0; s < alloc_count; s++)
|
||||
@ -1196,6 +1212,7 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
|
||||
ah->maximise_cling = find_config_tree_bool(cmd, allocation_maximise_cling_CFG, NULL);
|
||||
|
||||
ah->approx_alloc = approx_alloc;
|
||||
|
||||
return ah;
|
||||
}
|
||||
|
||||
@ -1216,19 +1233,11 @@ static int _sufficient_pes_free(struct alloc_handle *ah, struct dm_list *pvms,
|
||||
uint32_t free_pes = pv_maps_size(pvms);
|
||||
|
||||
if (total_extents_needed > free_pes) {
|
||||
if (!ah->approx_alloc) {
|
||||
log_error("Insufficient free space: %" PRIu32
|
||||
" extents needed,"
|
||||
log_error("Insufficient free space: %" PRIu32 " extents needed,"
|
||||
" but only %" PRIu32 " available",
|
||||
total_extents_needed, free_pes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
log_verbose("Insufficient free space: %" PRIu32
|
||||
" extents needed, but only %" PRIu32
|
||||
" available: amount will be reduced",
|
||||
total_extents_needed, free_pes);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -1429,7 +1438,7 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat
|
||||
if (area_len > alloc_state->areas[s].used)
|
||||
area_len = alloc_state->areas[s].used;
|
||||
|
||||
len = (ah->alloc_and_split_meta) ? total_area_count * 2 : total_area_count;
|
||||
len = (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) ? total_area_count * 2 : total_area_count;
|
||||
len *= sizeof(*aa);
|
||||
if (!(aa = dm_pool_alloc(ah->mem, len))) {
|
||||
log_error("alloced_area allocation failed");
|
||||
@ -1449,7 +1458,7 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat
|
||||
}
|
||||
|
||||
pva = alloc_state->areas[s + ix_log_skip].pva;
|
||||
if (ah->alloc_and_split_meta) {
|
||||
if (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) {
|
||||
/*
|
||||
* The metadata area goes at the front of the allocated
|
||||
* space for now, but could easily go at the end (or
|
||||
@ -1475,7 +1484,7 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat
|
||||
dm_list_add(&ah->alloced_areas[s], &aa[s].list);
|
||||
s -= ah->area_count + ah->parity_count;
|
||||
}
|
||||
aa[s].len = (ah->alloc_and_split_meta) ? len - ah->log_len : len;
|
||||
aa[s].len = (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) ? len - ah->log_len : len;
|
||||
/* Skip empty allocations */
|
||||
if (!aa[s].len)
|
||||
continue;
|
||||
@ -1493,7 +1502,8 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat
|
||||
}
|
||||
|
||||
/* Only need to alloc metadata from the first batch */
|
||||
ah->alloc_and_split_meta = 0;
|
||||
if (ah->alloc_and_split_meta)
|
||||
ah->split_metadata_is_allocated = 1;
|
||||
|
||||
ah->total_area_len += area_len;
|
||||
|
||||
@ -1995,7 +2005,8 @@ static void _reset_unreserved(struct dm_list *pvms)
|
||||
}
|
||||
|
||||
static void _report_needed_allocation_space(struct alloc_handle *ah,
|
||||
struct alloc_state *alloc_state)
|
||||
struct alloc_state *alloc_state,
|
||||
struct dm_list *pvms)
|
||||
{
|
||||
const char *metadata_type;
|
||||
uint32_t parallel_areas_count, parallel_area_size;
|
||||
@ -2003,7 +2014,7 @@ static void _report_needed_allocation_space(struct alloc_handle *ah,
|
||||
|
||||
parallel_area_size = ah->new_extents - alloc_state->allocated;
|
||||
parallel_area_size /= ah->area_multiple;
|
||||
parallel_area_size -= (ah->alloc_and_split_meta) ? ah->log_len : 0;
|
||||
parallel_area_size -= (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) ? ah->log_len : 0;
|
||||
|
||||
parallel_areas_count = ah->area_count + ah->parity_count;
|
||||
|
||||
@ -2011,14 +2022,16 @@ static void _report_needed_allocation_space(struct alloc_handle *ah,
|
||||
if (ah->alloc_and_split_meta) {
|
||||
metadata_type = "metadata area";
|
||||
metadata_count = parallel_areas_count;
|
||||
if (ah->split_metadata_is_allocated)
|
||||
metadata_size = 0;
|
||||
} else {
|
||||
metadata_type = "mirror log";
|
||||
metadata_count = alloc_state->log_area_count_still_needed;
|
||||
}
|
||||
|
||||
log_debug_alloc("Still need %s%" PRIu32 " total extents:",
|
||||
log_debug_alloc("Still need %s%" PRIu32 " total extents from %" PRIu32 " remaining:",
|
||||
ah->approx_alloc ? "up to " : "",
|
||||
parallel_area_size * parallel_areas_count + metadata_size * metadata_count);
|
||||
parallel_area_size * parallel_areas_count + metadata_size * metadata_count, pv_maps_size(pvms));
|
||||
log_debug_alloc(" %" PRIu32 " (%" PRIu32 " data/%" PRIu32
|
||||
" parity) parallel areas of %" PRIu32 " extents each",
|
||||
parallel_areas_count, ah->area_count, ah->parity_count, parallel_area_size);
|
||||
@ -2064,7 +2077,7 @@ static int _find_some_parallel_space(struct alloc_handle *ah, const struct alloc
|
||||
_clear_areas(alloc_state);
|
||||
_reset_unreserved(pvms);
|
||||
|
||||
_report_needed_allocation_space(ah, alloc_state);
|
||||
_report_needed_allocation_space(ah, alloc_state, pvms);
|
||||
|
||||
/* ix holds the number of areas found on other PVs */
|
||||
do {
|
||||
@ -2289,11 +2302,11 @@ static int _find_max_parallel_space_for_one_policy(struct alloc_handle *ah, stru
|
||||
* data together will be split, we must adjust
|
||||
* the comparison accordingly.
|
||||
*/
|
||||
if (ah->alloc_and_split_meta)
|
||||
if (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated)
|
||||
max_tmp -= ah->log_len;
|
||||
if (max_tmp > (spvs->le + spvs->len) * ah->area_multiple) {
|
||||
max_to_allocate = (spvs->le + spvs->len) * ah->area_multiple - alloc_state->allocated;
|
||||
max_to_allocate += ah->alloc_and_split_meta ? ah->log_len : 0;
|
||||
max_to_allocate += (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) ? ah->log_len : 0;
|
||||
}
|
||||
parallel_pvs = &spvs->pvs;
|
||||
break;
|
||||
@ -2321,7 +2334,7 @@ static int _find_max_parallel_space_for_one_policy(struct alloc_handle *ah, stru
|
||||
} else if (ah->maximise_cling && alloc_parms->alloc == ALLOC_NORMAL &&
|
||||
!(alloc_parms->flags & A_CLING_TO_ALLOCED))
|
||||
alloc_parms->flags |= A_CLING_TO_ALLOCED;
|
||||
} while ((alloc_parms->alloc != ALLOC_CONTIGUOUS) && alloc_state->allocated != alloc_parms->extents_still_needed && (alloc_parms->flags & A_CAN_SPLIT));
|
||||
} while ((alloc_parms->alloc != ALLOC_CONTIGUOUS) && alloc_state->allocated != alloc_parms->extents_still_needed && (alloc_parms->flags & A_CAN_SPLIT) && (!ah->approx_alloc || pv_maps_size(pvms)));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -2415,7 +2428,7 @@ static int _allocate(struct alloc_handle *ah,
|
||||
old_allocated = alloc_state.allocated;
|
||||
log_debug_alloc("Trying allocation using %s policy.", get_alloc_string(alloc));
|
||||
|
||||
if (!_sufficient_pes_free(ah, pvms, alloc_state.allocated, ah->new_extents))
|
||||
if (!ah->approx_alloc && !_sufficient_pes_free(ah, pvms, alloc_state.allocated, ah->new_extents))
|
||||
goto_out;
|
||||
|
||||
_init_alloc_parms(ah, &alloc_parms, alloc, prev_lvseg,
|
||||
@ -2441,12 +2454,19 @@ static int _allocate(struct alloc_handle *ah,
|
||||
ah->area_count / ah->area_multiple);
|
||||
goto out;
|
||||
}
|
||||
log_verbose("Insufficient suitable %sallocatable extents "
|
||||
"for logical volume %s: size reduced by %u extents",
|
||||
if (!alloc_state.allocated) {
|
||||
log_error("Insufficient suitable %sallocatable extents "
|
||||
"found for logical volume %s.",
|
||||
can_split ? "" : "contiguous ",
|
||||
lv ? lv->name : "");
|
||||
goto out;
|
||||
}
|
||||
log_verbose("Found fewer %sallocatable extents "
|
||||
"for logical volume %s than requested: using %" PRIu32 " extents (reduced by %u).",
|
||||
can_split ? "" : "contiguous ",
|
||||
lv ? lv->name : "",
|
||||
(ah->new_extents - alloc_state.allocated) *
|
||||
ah->area_count / ah->area_multiple);
|
||||
alloc_state.allocated,
|
||||
(ah->new_extents - alloc_state.allocated) * ah->area_count / ah->area_multiple);
|
||||
ah->new_extents = alloc_state.allocated;
|
||||
}
|
||||
|
||||
@ -3014,6 +3034,12 @@ static int _lv_extend_layered_lv(struct alloc_handle *ah,
|
||||
|
||||
/*
|
||||
* Entry point for single-step LV allocation + extension.
|
||||
* Extents is the number of logical extents to append to the LV unless
|
||||
* approx_alloc is set when it is an upper limit for the total number of
|
||||
* extents to use from the VG.
|
||||
*
|
||||
* FIXME The approx_alloc raid/stripe conversion should be performed
|
||||
* before calling this function.
|
||||
*/
|
||||
int lv_extend(struct logical_volume *lv,
|
||||
const struct segment_type *segtype,
|
||||
@ -3028,7 +3054,7 @@ int lv_extend(struct logical_volume *lv,
|
||||
struct alloc_handle *ah;
|
||||
uint32_t sub_lv_count;
|
||||
|
||||
log_very_verbose("Extending segment type, %s", segtype->name);
|
||||
log_very_verbose("Adding segment of type %s to LV %s.", segtype->name, lv->name);
|
||||
|
||||
if (segtype_is_virtual(segtype))
|
||||
return lv_add_virtual_segment(lv, 0u, extents, segtype, thin_pool_name);
|
||||
@ -3054,11 +3080,8 @@ int lv_extend(struct logical_volume *lv,
|
||||
|
||||
if (ah->approx_alloc) {
|
||||
extents = ah->new_extents;
|
||||
if (segtype_is_raid(segtype)) {
|
||||
log_error("Extents before: %u", extents);
|
||||
if (segtype_is_raid(segtype))
|
||||
extents -= ah->log_len * ah->area_multiple;
|
||||
log_error("Extents after : %u", extents);
|
||||
}
|
||||
}
|
||||
|
||||
if (segtype_is_thin_pool(segtype) || segtype_is_cache_pool(segtype)) {
|
||||
@ -6025,7 +6048,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
}
|
||||
|
||||
if (!seg_is_virtual(lp) &&
|
||||
vg->free_count < lp->extents) {
|
||||
vg->free_count < lp->extents && !lp->approx_alloc) {
|
||||
log_error("Volume group \"%s\" has insufficient free space "
|
||||
"(%u extents): %u required.",
|
||||
vg->name, vg->free_count, lp->extents);
|
||||
|
@ -140,7 +140,9 @@ int detach_thin_external_origin(struct lv_segment *seg)
|
||||
|
||||
int lv_is_merging_thin_snapshot(const struct logical_volume *lv)
|
||||
{
|
||||
return (first_seg(lv)->status & MERGING) ? 1 : 0;
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
|
||||
return (seg && seg->status & MERGING) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -363,21 +363,19 @@ static int _update_extents_params(struct volume_group *vg,
|
||||
} else
|
||||
lp->pvh = &vg->pvs;
|
||||
|
||||
if (lcp->percent)
|
||||
lp->approx_alloc = 1;
|
||||
switch(lcp->percent) {
|
||||
case PERCENT_VG:
|
||||
lp->extents = percent_of_extents(lp->extents, vg->extent_count, 0);
|
||||
extents = percent_of_extents(lp->extents, vg->extent_count, 0);
|
||||
break;
|
||||
case PERCENT_FREE:
|
||||
lp->extents = percent_of_extents(lp->extents, vg->free_count, 0);
|
||||
extents = percent_of_extents(lp->extents, vg->free_count, 0);
|
||||
break;
|
||||
case PERCENT_PVS:
|
||||
if (lcp->pv_count) {
|
||||
pv_extent_count = pv_list_extents_free(lp->pvh);
|
||||
lp->extents = percent_of_extents(lp->extents, pv_extent_count, 0);
|
||||
extents = percent_of_extents(lp->extents, pv_extent_count, 0);
|
||||
} else
|
||||
lp->extents = percent_of_extents(lp->extents, vg->extent_count, 0);
|
||||
extents = percent_of_extents(lp->extents, vg->extent_count, 0);
|
||||
break;
|
||||
case PERCENT_LV:
|
||||
log_error("Please express size as %%VG, %%PVS, or "
|
||||
@ -394,12 +392,19 @@ static int _update_extents_params(struct volume_group *vg,
|
||||
log_error(INTERNAL_ERROR "Couldn't find origin volume.");
|
||||
return 0;
|
||||
}
|
||||
lp->extents = percent_of_extents(lp->extents, origin->le_count, 0);
|
||||
extents = percent_of_extents(lp->extents, origin->le_count, 0);
|
||||
break;
|
||||
case PERCENT_NONE:
|
||||
extents = lp->extents;
|
||||
break;
|
||||
}
|
||||
|
||||
if (lcp->percent) {
|
||||
lp->approx_alloc = 1;
|
||||
log_verbose("Converted %" PRIu32 "%%%s into %" PRIu32 " extents.", lp->extents, get_percent_string(lcp->percent), extents);
|
||||
lp->extents = extents;
|
||||
}
|
||||
|
||||
if (lp->snapshot && lp->origin && lp->extents) {
|
||||
if (!lp->chunk_size) {
|
||||
log_error(INTERNAL_ERROR "Missing snapshot chunk size.");
|
||||
|
Loading…
Reference in New Issue
Block a user