From: David Teigland Date: Fri, 9 Sep 2022 21:07:07 +0000 (-0500) Subject: lvresize: restructure code X-Git-Tag: v2_03_17~120 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=18722dfdf4d3;p=lvm2.git lvresize: restructure code Rewrite top level resize function to prepare for adding new fs resizing. --- diff --git a/lib/commands/cmd_enum.h b/lib/commands/cmd_enum.h new file mode 100644 index 000000000..939af81c3 --- /dev/null +++ b/lib/commands/cmd_enum.h @@ -0,0 +1,19 @@ +#ifndef _CMD_ENUM_H +#define _CMD_ENUM_H + +/* + * tools/cmds.h is generated by the Makefile. For each command definition + * in command-lines.in, cmds.h contains: + * cmd(foo_CMD, foo) + * + * This header adds each of the foo_CMD's into an enum, so there's + * a unique integer identifier for each command definition. + */ + +enum { +#define cmd(a, b) a , +#include "../tools/cmds.h" +#undef cmd +}; + +#endif diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h index 7a4979b33..eb2d1922b 100644 --- a/lib/commands/toolcontext.h +++ b/lib/commands/toolcontext.h @@ -18,6 +18,7 @@ #include "lib/device/dev-cache.h" #include "lib/device/dev-type.h" +#include "lib/commands/cmd_enum.h" #include @@ -94,6 +95,7 @@ struct cmd_context { const char *name; /* needed before cmd->command is set */ struct command_name *cname; struct command *command; + int command_enum; /* duplicate from command->command_enum for lib code */ char **argv; struct arg_values *opt_arg_values; struct dm_list arg_value_groups; diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c index f0183db3d..2ef0900d4 100644 --- a/lib/locking/lvmlockd.c +++ b/lib/locking/lvmlockd.c @@ -577,8 +577,9 @@ static int _extend_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg, (uint32_t)(new_size_sectors * SECTOR_SIZE)); lp.size = new_size_sectors; + lp.pvh = &vg->pvs; - if (!lv_resize(lv, &lp, &vg->pvs)) { + if (!lv_resize(cmd, lv, &lp)) { log_error("Extend sanlock LV %s to size %s failed.", display_lvname(lv), display_size(cmd, lp.size)); return 0; @@ -2733,6 +2734,9 @@ int lockd_lv_resize(struct cmd_context *cmd, struct logical_volume *lv, if (!_lvmlockd_connected) return 0; + if (lv_is_lockd_sanlock_lv(lv)) + return 1; + /* * A special case for gfs2 where we want to allow lvextend * of an LV that has an existing shared lock, which is normally diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index a9057830d..e819db8d5 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -4935,8 +4935,8 @@ static int _validate_stripesize(const struct volume_group *vg, return 1; } -static int _request_confirmation(const struct logical_volume *lv, - const struct lvresize_params *lp) +static int _lv_reduce_confirmation(struct logical_volume *lv, + struct lvresize_params *lp) { const struct volume_group *vg = lv->vg; struct lvinfo info = { 0 }; @@ -4946,15 +4946,7 @@ static int _request_confirmation(const struct logical_volume *lv, return 0; } - if (lp->resizefs) { - if (!info.exists) { - log_error("Logical volume %s must be activated " - "before resizing filesystem.", - display_lvname(lv)); - return 0; - } - return 1; - } else if (lv_is_vdo(lv) && !info.exists) { + if (lv_is_vdo(lv) && !info.exists) { log_error("Logical volume %s must be activated " "before reducing device size.", display_lvname(lv)); @@ -5064,8 +5056,9 @@ static uint32_t _adjust_amount(dm_percent_t percent, int policy_threshold, int p return (policy_amount < percent) ? (uint32_t) percent : (uint32_t) policy_amount; } -static int _lvresize_adjust_policy(const struct logical_volume *lv, - uint32_t *amount, uint32_t *meta_amount) +/* "amount" here is percent */ +int lv_extend_policy_calculate_percent(struct logical_volume *lv, + uint32_t *amount, uint32_t *meta_amount) { struct cmd_context *cmd = lv->vg->cmd; dm_percent_t percent; @@ -5111,8 +5104,10 @@ static int _lvresize_adjust_policy(const struct logical_volume *lv, } } - if (policy_threshold >= 100) + if (policy_threshold >= 100) { + log_debug("lvextend policy disabled by threshold 100"); return 1; /* nothing to do */ + } if (!policy_amount) { log_error("Can't extend %s with %s autoextend percent set to 0%%.", @@ -5148,6 +5143,8 @@ static int _lvresize_adjust_policy(const struct logical_volume *lv, *amount = _adjust_amount(percent, policy_threshold, policy_amount); + log_debug("lvextend policy calculated percentages main %u meta %u from threshold %d percent %d", + *amount, *meta_amount, policy_threshold, policy_amount); return 1; } @@ -5184,120 +5181,6 @@ static uint32_t _lvseg_get_stripes(struct lv_segment *seg, uint32_t *stripesize) return 0; } -static int _lvresize_check(struct logical_volume *lv, - struct lvresize_params *lp) -{ - struct volume_group *vg = lv->vg; - struct lv_segment *seg = first_seg(lv); - - if (lv_is_external_origin(lv)) { - /* - * Since external-origin can be activated read-only, - * there is no way to use extended areas. - */ - log_error("Cannot resize external origin logical volume %s.", - display_lvname(lv)); - return 0; - } - - if (lv_is_raid_image(lv) || lv_is_raid_metadata(lv)) { - log_error("Cannot resize a RAID %s directly", - lv_is_raid_image(lv) ? "image" : "metadata area"); - return 0; - } - - if (lv_is_raid_with_tracking(lv)) { - log_error("Cannot resize logical volume %s while it is " - "tracking a split image.", display_lvname(lv)); - return 0; - } - - if ((seg_is_raid4(seg) || seg_is_any_raid5(seg)) && seg->area_count < 3) { - log_error("Cannot resize %s LV %s. Convert to more stripes first.", - lvseg_name(seg), display_lvname(lv)); - return 0; - } - - if (lv_is_raid(lv) && - lp->resize == LV_REDUCE) { - unsigned attrs = 0; - const struct segment_type *segtype = first_seg(lv)->segtype; - - if (!segtype->ops->target_present || - !segtype->ops->target_present(lv->vg->cmd, NULL, &attrs) || - !(attrs & RAID_FEATURE_SHRINK)) { - log_error("RAID module does not support shrinking."); - return 0; - } - } - - if (!lv_is_visible(lv) && - !lv_is_thin_pool_metadata(lv) && - !lv_is_vdo_pool_data(lv) && - !lv_is_lockd_sanlock_lv(lv)) { - log_error("Can't resize internal logical volume %s.", display_lvname(lv)); - return 0; - } - - if (lv_is_locked(lv)) { - log_error("Can't resize locked logical volume %s.", display_lvname(lv)); - return 0; - } - - if (lv_is_converting(lv)) { - log_error("Can't resize logical volume %s while " - "lvconvert in progress.", display_lvname(lv)); - return 0; - } - - if (lp->stripe_size) { - if (!(vg->fid->fmt->features & FMT_SEGMENTS)) { - log_print_unless_silent("Varied stripesize not supported. Ignoring."); - lp->stripe_size = lp->stripes = 0; - } else if (!_validate_stripesize(vg, lp)) - return_0; - } - - if (lp->resizefs && - (lv_is_thin_pool(lv) || - lv_is_thin_pool_data(lv) || - lv_is_thin_pool_metadata(lv) || - lv_is_vdo_pool(lv) || - lv_is_vdo_pool_data(lv) || - lv_is_pool_metadata_spare(lv) || - lv_is_lockd_sanlock_lv(lv))) { - log_print_unless_silent("Ignoring --resizefs as volume %s does not have a filesystem.", - display_lvname(lv)); - lp->resizefs = 0; - } - - if (lp->stripes && - !(vg->fid->fmt->features & FMT_SEGMENTS)) { - log_print_unless_silent("Varied striping not supported. Ignoring."); - lp->stripes = 0; - } - - if (lp->mirrors && - !(vg->fid->fmt->features & FMT_SEGMENTS)) { - log_print_unless_silent("Mirrors not supported. Ignoring."); - lp->mirrors = 0; - } - - if (lv_component_is_active(lv)) { - log_error("Cannot resize logical volume %s with active component LV(s).", - display_lvname(lv)); - return 0; - } - - if (lv_is_integrity(lv) || lv_raid_has_integrity(lv)) { - if (lp->resize == LV_REDUCE) { - log_error("Cannot reduce LV with integrity."); - return 0; - } - } - return 1; -} - static int _lvresize_adjust_size(struct volume_group *vg, uint64_t size, sign_t sign, uint32_t *extents) @@ -5327,17 +5210,23 @@ static int _lvresize_adjust_size(struct volume_group *vg, /* * If percent options were used, convert them into actual numbers of extents. + * FIXME: fix cases where lp->extents is initially used as a percentage, + * and is then rewritten to be a number of extents (simply save the percent + * value elsewhere.) */ static int _lvresize_extents_from_percent(const struct logical_volume *lv, - struct lvresize_params *lp, - struct dm_list *pvh) + struct lvresize_params *lp) { const struct volume_group *vg = lv->vg; uint32_t pv_extent_count; uint32_t old_extents = lp->extents; + log_debug("lvresize_extents_from_percent type %d extents %u percent_value %u", + lp->percent, lp->extents, lp->percent_value); + switch (lp->percent) { case PERCENT_VG: + /* rewrites lp->extents from percentage to extents */ lp->extents = percent_of_extents(lp->extents, vg->extent_count, (lp->sign != SIGN_MINUS)); if ((lp->sign == SIGN_NONE) && (lp->extents > (lv->le_count + vg->free_count))) { @@ -5348,21 +5237,45 @@ static int _lvresize_extents_from_percent(const struct logical_volume *lv, } break; case PERCENT_FREE: + /* rewrites lp->extents from percentage to extents */ lp->extents = percent_of_extents(lp->extents, vg->free_count, (lp->sign != SIGN_MINUS)); break; case PERCENT_LV: - lp->extents = percent_of_extents(lp->extents, lv->le_count, - (lp->sign != SIGN_MINUS)); + if (lp->extents) { + /* rewrites lp->extents from percentage to extents */ + lp->extents = percent_of_extents(lp->extents, lv->le_count, + (lp->sign != SIGN_MINUS)); + } else if (lp->percent_value) { + old_extents = lp->percent_value; + lp->extents = percent_of_extents(lp->percent_value, lv->le_count, + (lp->sign != SIGN_MINUS)); + } break; case PERCENT_PVS: - if (pvh != &vg->pvs) { - pv_extent_count = pv_list_extents_free(pvh); - lp->extents = percent_of_extents(lp->extents, pv_extent_count, + if (lp->pvh != &vg->pvs) { + pv_extent_count = pv_list_extents_free(lp->pvh); + if (lp->extents) { + /* rewrites lp->extents from percentage to extents */ + lp->extents = percent_of_extents(lp->extents, pv_extent_count, (lp->sign != SIGN_MINUS)); - } else - lp->extents = percent_of_extents(lp->extents, vg->extent_count, + } else if (lp->percent_value) { + /* lvresize has PVs args and no size of exents options */ + old_extents = lp->percent_value; + lp->extents = percent_of_extents(lp->percent_value, pv_extent_count, (lp->sign != SIGN_MINUS)); + } + } else { + if (lp->extents) { + /* rewrites lp->extents from percentage to extents */ + lp->extents = percent_of_extents(lp->extents, vg->extent_count, + (lp->sign != SIGN_MINUS)); + } else if (lp->percent_value) { + old_extents = lp->percent_value; + lp->extents = percent_of_extents(lp->percent_value, vg->extent_count, + (lp->sign != SIGN_MINUS)); + } + } break; case PERCENT_ORIGIN: if (!lv_is_cow(lv)) { @@ -5430,7 +5343,7 @@ static uint32_t _lv_pe_count(struct logical_volume *lv) /* FIXME Avoid having variables like lp->extents mean different things at different places */ static int _lvresize_adjust_extents(struct logical_volume *lv, struct lvresize_params *lp, - struct dm_list *pvh) + int *matches_existing) { struct volume_group *vg = lv->vg; struct cmd_context *cmd = vg->cmd; @@ -5711,12 +5624,12 @@ static int _lvresize_adjust_extents(struct logical_volume *lv, } if ((lp->extents == existing_logical_extents) && !lp->use_policies) { - if (!lp->resizefs && !lp->poolmetadata_size) { - log_error("New size (%d extents) matches existing size (%d extents).", - lp->extents, existing_logical_extents); - return 0; - } - lp->resize = LV_EXTEND; /* lets pretend zero size extension */ + log_print("New size (%d extents) matches existing size (%d extents).", + lp->extents, existing_logical_extents); + if (lp->resize == LV_ANY) + lp->resize = LV_EXTEND; /* lets pretend zero size extension */ + *matches_existing = 1; + return 1; } /* Perform any rounding to produce complete stripes. */ @@ -5771,12 +5684,12 @@ static int _lvresize_adjust_extents(struct logical_volume *lv, } lp->resize = LV_EXTEND; } else if ((lp->extents == existing_logical_extents) && !lp->use_policies) { - if (!lp->resizefs && !lp->poolmetadata_size) { - log_error("New size (%d extents) matches existing size " - "(%d extents)", lp->extents, existing_logical_extents); - return 0; - } - lp->resize = LV_EXTEND; + log_print("New size (%d extents) matches existing size (%d extents)", + lp->extents, existing_logical_extents); + if (lp->resize == LV_ANY) + lp->resize = LV_EXTEND; + *matches_existing = 1; + return 1; } /* @@ -5794,8 +5707,52 @@ static int _lvresize_adjust_extents(struct logical_volume *lv, return 1; } -static int _lvresize_check_type(const struct logical_volume *lv, - const struct lvresize_params *lp) +static int _lv_reduce_vdo_discard(struct cmd_context *cmd, + struct logical_volume *lv, + struct lvresize_params *lp) +{ + char name[PATH_MAX]; + struct device *dev; + struct volume_group *vg = lv->vg; + + /* FIXME: stop using dev-cache and struct device here, dev-cache + should only be used for scanning headers/metadata to find PVs. */ + + if (dm_snprintf(name, sizeof(name), "%s%s/%s", cmd->dev_dir, + vg->name, lv->name) < 0) { + log_error("Name too long - device not discarded (%s)", lv->name); + return 0; + } + + if (!(dev = dev_cache_get(cmd, name, NULL))) { + log_error("%s: not found: device not discarded.", name); + return 0; + } + + if (!dev_discard_max_bytes(cmd->dev_types, dev) || + !dev_discard_granularity(cmd->dev_types, dev)) { + log_error("%s: max bytes and granularity query fails.", name); + dev_destroy_file(dev); + return 0; + } + + log_warn("WARNING: %s: Discarding %s at offset " FMTu64 ", please wait...", + name, display_size(cmd, (uint64_t)(lv->le_count - lp->extents) * vg->extent_size), + ((uint64_t)lp->extents * vg->extent_size) << SECTOR_SHIFT); + + if (!dev_discard_blocks(dev, ((uint64_t)lp->extents * vg->extent_size) << SECTOR_SHIFT, + ((uint64_t)(lv->le_count - lp->extents) * vg->extent_size) << SECTOR_SHIFT)) { + log_error("%s: discarding failed.", name); + dev_destroy_file(dev); + return 0; + } + + dev_destroy_file(dev); + return 1; +} + +static int _lv_resize_check_type(struct logical_volume *lv, + struct lvresize_params *lp) { struct lv_segment *seg; @@ -5812,6 +5769,20 @@ static int _lvresize_check_type(const struct logical_volume *lv, } } + if (lv_is_raid_image(lv) || lv_is_raid_metadata(lv)) { + log_error("Cannot resize a RAID %s directly for %s", + lv_is_raid_image(lv) ? "image" : "metadata area", + display_lvname(lv)); + return 0; + } + + seg = first_seg(lv); + if ((seg_is_raid4(seg) || seg_is_any_raid5(seg)) && seg->area_count < 3) { + log_error("Cannot resize %s LV %s. Convert to more stripes first.", + lvseg_name(seg), display_lvname(lv)); + return 0; + } + if (lp->resize == LV_REDUCE) { if (lv_is_thin_pool_data(lv)) { log_error("Thin pool volumes %s cannot be reduced in size yet.", @@ -5832,6 +5803,21 @@ static int _lvresize_check_type(const struct logical_volume *lv, log_error("Reduce not yet allowed on LVs with writecache attached."); return 0; } + if (lv_is_raid(lv)) { + unsigned attrs = 0; + const struct segment_type *segtype = first_seg(lv)->segtype; + + if (!segtype->ops->target_present || + !segtype->ops->target_present(lv->vg->cmd, NULL, &attrs) || + !(attrs & RAID_FEATURE_SHRINK)) { + log_error("RAID module does not support shrinking."); + return 0; + } + } + if (lv_is_integrity(lv) || lv_raid_has_integrity(lv)) { + log_error("Cannot reduce LV with integrity."); + return 0; + } } else if (lp->resize == LV_EXTEND) { if (lv_is_thin_pool_metadata(lv) && (!(seg = find_pool_seg(first_seg(lv))) || @@ -5856,12 +5842,15 @@ static int _lvresize_check_type(const struct logical_volume *lv, return 0; } + if ((lp->resize == LV_REDUCE) && (lp->pvh != &lv->vg->pvs)) + log_print_unless_silent("Ignoring PVs on command line when reducing."); + return 1; } -static int _lvresize_volume(struct logical_volume *lv, - struct lvresize_params *lp, - struct dm_list *pvh) +static int _lv_resize_volume(struct logical_volume *lv, + struct lvresize_params *lp, + struct dm_list *pvh) { struct volume_group *vg = lv->vg; struct cmd_context *cmd = vg->cmd; @@ -5895,24 +5884,32 @@ static int _lvresize_volume(struct logical_volume *lv, display_lvname(lv), display_size(cmd, (uint64_t) old_extents * vg->extent_size), old_extents, display_size(cmd, (uint64_t) lv->le_count * vg->extent_size), lv->le_count); - - /* Resizing metadata and PV list is not specified -> maintain size of _pmspare volume */ - if ((&vg->pvs == pvh) && lv_is_pool_metadata(lv)) - (void) handle_pool_metadata_spare(vg, 0, pvh, 1); } return 1; } -static int _lvresize_prepare(struct logical_volume *lv, - struct lvresize_params *lp, - struct dm_list *pvh) +static int _lv_resize_adjust_size(struct logical_volume *lv, + struct lvresize_params *lp, + int *matches_existing) { /* Resolve extents from size */ - if (lp->size && !_lvresize_adjust_size(lv->vg, lp->size, lp->sign, &lp->extents)) - return_0; - else if (lp->extents && !_lvresize_extents_from_percent(lv, lp, pvh)) - return_0; + if (lp->size) { + if (!_lvresize_adjust_size(lv->vg, lp->size, lp->sign, &lp->extents)) + return_0; + } + + /* set lp->extents based on lp->percent_value */ + else if (lp->percent_value) { + if (!_lvresize_extents_from_percent(lv, lp)) + return_0; + } + + /* rewrites lp->extents from percentage to extents */ + else if (lp->extents && (lp->percent != PERCENT_NONE)) { + if (!_lvresize_extents_from_percent(lv, lp)) + return_0; + } /* Ensure stripe boundary extents! */ if (!lp->percent && lv_is_raid(lv)) @@ -5920,18 +5917,15 @@ static int _lvresize_prepare(struct logical_volume *lv, seg_is_raid1(first_seg(lv)) ? 0 : _raid_stripes_count(first_seg(lv)), lp->resize == LV_REDUCE ? 0 : 1); - if (!_lvresize_adjust_extents(lv, lp, pvh)) - return_0; - - if (!_lvresize_check_type(lv, lp)) + if (!_lvresize_adjust_extents(lv, lp, matches_existing)) return_0; return 1; } -/* Set aux LV properties, we can't use those from command line */ -static struct logical_volume *_lvresize_setup_aux(struct logical_volume *lv, - struct lvresize_params *lp) +/* Set thin pool metadata properties, we can't use those from command line */ +static void _setup_params_for_extend_metadata(struct logical_volume *lv, + struct lvresize_params *lp) { struct lv_segment *mseg = last_seg(lv); @@ -5942,272 +5936,466 @@ static struct logical_volume *_lvresize_setup_aux(struct logical_volume *lv, lp->resizefs = 0; lp->stripes = lp->mirrors ? mseg->area_count / lp->mirrors : 0; lp->stripe_size = mseg->stripe_size; +} - return lv; + +static int _lv_resize_check_used(struct logical_volume *lv) +{ + if (lv_is_locked(lv)) { + log_error("Can't resize locked logical volume %s.", display_lvname(lv)); + return 0; + } + + if (lv_is_converting(lv)) { + log_error("Can't resize logical volume %s while lvconvert in progress.", display_lvname(lv)); + return 0; + } + + if (lv_component_is_active(lv)) { + log_error("Cannot resize logical volume %s with active component LV(s).", display_lvname(lv)); + return 0; + } + + if (lv_is_raid_with_tracking(lv)) { + log_error("Cannot resize logical volume %s while it is tracking a split image.", display_lvname(lv)); + return 0; + } + + if (lv_is_external_origin(lv)) { + /* + * Since external-origin can be activated read-only, + * there is no way to use extended areas. + */ + log_error("Cannot resize external origin logical volume %s.", + display_lvname(lv)); + return 0; + } + + return 1; } -int lv_resize(struct logical_volume *lv, - struct lvresize_params *lp, - struct dm_list *pvh) +int lv_resize(struct cmd_context *cmd, struct logical_volume *lv, + struct lvresize_params *lp) { + struct lvresize_params lp_meta; struct volume_group *vg = lv->vg; - struct cmd_context *cmd = vg->cmd; - struct logical_volume *lock_lv = (struct logical_volume*) lv_lock_holder(lv); - struct logical_volume *aux_lv = NULL; /* Note: aux_lv never resizes fs */ - struct logical_volume *resizable_layer_lv; - struct lvresize_params aux_lp; struct lv_segment *seg = first_seg(lv); + struct logical_volume *lv_top = NULL; + struct logical_volume *lv_main = NULL; + struct logical_volume *lv_meta = NULL; + struct logical_volume *lv_main_layer = NULL; + struct logical_volume *lv_meta_layer = NULL; + int main_size_matches = 0; + int meta_size_matches = 0; + int is_extend = (lp->resize == LV_EXTEND); + int is_reduce = (lp->resize == LV_REDUCE); int activated = 0; - int ret = 0; int status; - struct device *dev; - char name[PATH_MAX]; + int ret = 0; + + memset(&lp_meta, 0, sizeof(lp_meta)); + + /* + * Some checks apply to the LV command arg (don't require top/bottom + * LVs in a stack), and don't require knowing if the command is doing + * extend or reduce (determined later). + */ + + if (lp->stripe_size && !_validate_stripesize(vg, lp)) + return_0; + + /* + * The only types of !visible/internal/non-top LVs that can be directly + * resized via the command arg. Other internal LVs are resized + * indirectly when resizing a top LV. + */ + if (!lv_is_visible(lv) && + !lv_is_thin_pool_data(lv) && + !lv_is_thin_pool_metadata(lv) && + !lv_is_vdo_pool_data(lv) && + !lv_is_lockd_sanlock_lv(lv)) { + log_error("Can't resize internal logical volume %s.", display_lvname(lv)); + return 0; + } - if (lp->use_policies) { - if (!lv_is_cow(lv) && - !lv_is_thin_pool(lv) && - !lv_is_vdo_pool(lv)) { - log_error("Policy-based resize is supported only for snapshot, thin pool and vdo pool volumes."); + /* + * resizefs applies to the LV command arg, not to the top LV or lower + * resizable LV. + */ + if (lp->resizefs) { + if (!lv_is_active(lv)) { + log_error("Logical volume %s must be activated before resizing filesystem.", + display_lvname(lv)); return 0; } + /* types of LVs that can hold a file system */ + if (!(lv_is_linear(lv) || lv_is_striped(lv) || lv_is_raid(lv) || + lv_is_mirror(lv) || lv_is_thin_volume(lv) || lv_is_vdo(lv) || + lv_is_cache(lv) || lv_is_writecache(lv))) { + log_print_unless_silent("Ignoring --resizefs for LV type %s.", + seg ? seg->segtype->name : "unknown"); + lp->resizefs = 0; + } + } - lp->extents = 0; - lp->sign = SIGN_PLUS; - lp->percent = PERCENT_LV; + /* + * Figure out which LVs are going to be extended, and set params + * to the requested extents/size for each. Some LVs are extended + * only by extending an underlying LV. Extending some top level + * LVs results in extending multiple underlying LVs. + * + * lv_top is the top level LV in stack. + * lv_main is the main LV to be resized. + * lv_meta is always a thin pool metadata LV. + * + * lv_main_layer/lv_meta_layer may be LV types (like cache) that are + * layered over the main/meta LVs. These layer LVs are skipped over + * by get_resizable_layer_lv() which finds the bottom-most layer + * which is originally resized. The layer LVs are resized indirectly + * as a result of the lower data-holding LVs being resized. + * + * In the simplest case there is no layering/stacking, and + * lv == lv_main == lv_main_layer == lv_top + */ - aux_lp = *lp; - if (!_lvresize_adjust_policy(lv, &lp->extents, &aux_lp.extents)) + if (cmd->command_enum == lvextend_policy_CMD) { + /* lvextend --use-policies may extend main or meta or both */ + lv_top = lv; + if (lv_is_thin_pool(lv)) { + if (lp->policy_percent_main) { + lv_main = seg_lv(first_seg(lv), 0); /* thin pool data */ + lp->percent_value = lp->policy_percent_main; + } + if (lp->policy_percent_meta) { + lv_meta = first_seg(lv)->metadata_lv; /* thin pool metadata */ + _setup_params_for_extend_metadata(lv_meta, &lp_meta); + /* override setup function which isn't right for policy use */ + lp_meta.percent = PERCENT_LV; + lp_meta.sign = SIGN_PLUS; + lp_meta.percent_value = lp->policy_percent_meta; + lp_meta.pvh = lp->pvh; + } + } else if (lv_is_vdo_pool(lv)) { + lv_main = seg_lv(first_seg(lv), 0); /* vdo pool data */ + lp->percent_value = lp->policy_percent_main; + } else if (lv_is_cow(lv)) { + lv_main = lv; + lp->percent_value = lp->policy_percent_main; + } else return_0; - if (!lp->extents) { - if (!aux_lp.extents) - return 1; /* Nothing to do */ - /* Resize thin-pool metadata as mainlv */ - lv = seg->metadata_lv; /* metadata LV */ - lp->extents = aux_lp.extents; - } else if (aux_lp.extents) { - /* Also resize thin-pool metadata */ - aux_lv = _lvresize_setup_aux(seg->metadata_lv, &aux_lp); - } - } else if (lp->poolmetadata_size) { - /* Validate --poolmetadata_size supported volumes here */ - if (!lv_is_thin_pool(lv) && !lv_is_thin_pool_metadata(lv)) { + } else if ((cmd->command_enum == lvextend_pool_metadata_CMD) || + (cmd->command_enum == lvresize_pool_metadata_CMD)) { + /* lvresize|lvextend --poolmetadatasize, extends only thin pool metadata */ + if (lv_is_thin_pool(lv)) { + lv_top = lv; + lv_meta = first_seg(lv)->metadata_lv; /* thin pool metadata */ + } else if (lv_is_thin_pool_metadata(lv)) { + lv_top = _get_top_layer_lv(lv); /* thin pool LV */ + lv_meta = lv; + } else { log_error("--poolmetadatasize can be used only with thin pools."); return 0; } - if (!lp->extents && !lp->size) { - /* When only --poolmetadatasize given and any size option - * switch directly to resize metadata LV */ - if (lv_is_thin_pool(lv)) - lv = seg->metadata_lv; - lp->size = lp->poolmetadata_size; - lp->sign = lp->poolmetadata_sign; + lp_meta = *lp; + _setup_params_for_extend_metadata(lv_meta, &lp_meta); + lp_meta.size = lp->poolmetadata_size; + lp_meta.sign = lp->poolmetadata_sign; + lp->poolmetadata_size = 0; + lp->poolmetadata_sign = 0; + + } else if (lv_is_thin_pool(lv) && lp->poolmetadata_size) { + /* extend both thin pool data and metadata */ + lv_top = lv; + lv_main = seg_lv(first_seg(lv), 0); /* thin pool data */ + lv_meta = first_seg(lv)->metadata_lv; /* thin pool metadata */ + lp_meta = *lp; + _setup_params_for_extend_metadata(lv_meta, &lp_meta); + lp_meta.size = lp->poolmetadata_size; + lp_meta.sign = lp->poolmetadata_sign; + lp->poolmetadata_size = 0; + lp->poolmetadata_sign = 0; + + } else if (lv_is_thin_pool_metadata(lv)) { + /* extend only thin pool metadata */ + lv_top = _get_top_layer_lv(lv); /* thin pool LV */ + lv_meta = lv; + lp_meta = *lp; + _setup_params_for_extend_metadata(lv_meta, &lp_meta); + if (lp->poolmetadata_size) { + lp_meta.size = lp->poolmetadata_size; + lp_meta.size = lp->poolmetadata_sign; lp->poolmetadata_size = 0; - } else if (lv_is_thin_pool(lv)) { - aux_lp = *lp; - aux_lv = _lvresize_setup_aux(seg->metadata_lv, &aux_lp); - aux_lp.size = lp->poolmetadata_size; - aux_lp.sign = lp->poolmetadata_sign; - } else { - log_error("--poolmetadatasize for thin-pool metadata cannot be used with size."); - return 0; + lp->poolmetadata_sign = 0; } + /* else lp_meta.extents|size from lp->extents|size above */ + + } else if (lv_is_thin_pool(lv)) { + /* extend thin pool data and possibly metadata */ + lv_top = lv; + lv_main = seg_lv(first_seg(lv), 0); + /* Do not set lv_meta to the thin pool metadata here. + See below "Possibly enable lv_meta extend". */ } - resizable_layer_lv = _get_resizable_layer_lv(lv); - if (!(lv = _get_top_layer_lv(lv))) - return_0; + /* + * None of the special cases above (selecting which LVs to extend + * depending on options set and type of LV) have applied, so this + * is the standard case. + */ + if (!lv_main && !lv_meta) { + lv_top = _get_top_layer_lv(lv); + lv_main_layer = lv; + lv_main = _get_resizable_layer_lv(lv_main_layer); + } else { + lv_main_layer = lv_main; + lv_meta_layer = lv_meta; + if (lv_main) + lv_main = _get_resizable_layer_lv(lv_main_layer); + if (lv_meta) + lv_meta = _get_resizable_layer_lv(lv_meta_layer); + } + /* Clear layer variables if no layer exists. */ + if (lv_main_layer == lv_main) + lv_main_layer = NULL; + if (lv_meta_layer == lv_meta) + lv_meta_layer = NULL; - if (!_lvresize_check(lv, lp)) - return_0; + /* + * LVs to work with are now determined: + * lv_top is always set, it is not used to resize, but is used + * to reload dm devices for the lv. + * If lv_main is set, it is resized. + * If lv_meta is set, it is resized. + * If lv_meta is not set, it may be set below and resized. + */ - /* Always should have lp->size or lp->extents */ - if (!_lvresize_prepare(resizable_layer_lv, lp, pvh)) + if (!_lv_resize_check_used(lv_top)) return_0; - - if ((lp->resize != LV_REDUCE) && !aux_lv && !lp->poolmetadata_size && - (&vg->pvs == pvh) && lv_is_thin_pool(lv)) { - /* When thin-pool data part is extended, automatically extend also metadata part - * to have the metadata chunks for adressing all data blocks - * Do this only when PV list is not defined and --poolmetadatasize is unspecified */ - aux_lp = *lp; - seg = first_seg(lv); - aux_lp.size = estimate_thin_pool_metadata_size(lp->extents, vg->extent_size, seg->chunk_size); - if (aux_lp.size > seg->metadata_lv->size) { - log_verbose("Also going to resize thin-pool metadata to match bigger data."); - aux_lv = _lvresize_setup_aux(seg->metadata_lv, &aux_lp); - aux_lp.sign = SIGN_NONE; - } else - aux_lp.size = 0; - } - - if (aux_lv && (aux_lv = _get_resizable_layer_lv(aux_lv)) && - !_lvresize_prepare(aux_lv, &aux_lp, pvh)) + if (lv_main && (lv_main != lv_top) && !_lv_resize_check_used(lv_main)) return_0; - if (((lp->resize == LV_REDUCE) || - (aux_lv && aux_lp.resize == LV_REDUCE)) && - (pvh != &vg->pvs)) - log_print_unless_silent("Ignoring PVs on command line when reducing."); + /* + * Set a new size for lv_main. + */ + if (lv_main) { + /* sets lp extents and lp resize */ + if (!_lv_resize_adjust_size(lv_main, lp, &main_size_matches)) + return_0; + /* sanity check the result of adjust_size */ + if (lp->extents == 0) + return_0; + /* adjust_size resolves LV_ANY to EXTEND|REDUCE */ + if (lp->resize == LV_ANY) + return_0; + if (is_extend && (lp->resize != LV_EXTEND)) + return_0; + if (is_reduce && (lp->resize != LV_REDUCE)) + return_0; + is_extend = (lp->resize == LV_EXTEND); + is_reduce = (lp->resize == LV_REDUCE); - /* Request confirmation before operations that are often mistakes. */ - /* aux_lv never resize fs */ - if ((lp->resizefs || (lp->resize == LV_REDUCE)) && - !_request_confirmation(lv, lp)) - return_0; + if (!_lv_resize_check_type(lv_main, lp)) + return_0; - if (lp->resizefs) { - if (!lp->nofsck && - !_fsadm_cmd(FSADM_CMD_CHECK, lv, 0, lp->yes, lp->force, &status)) { - if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) { - log_error("Filesystem check failed."); - return 0; - } - /* some filesystems support online resize */ - } + if (is_reduce && !main_size_matches && !lp->resizefs && + !_lv_reduce_confirmation(lv, lp)) + return_0; + } - /* FIXME forks here */ - if ((lp->resize == LV_REDUCE) && - !_fsadm_cmd(FSADM_CMD_RESIZE, lv, lp->extents, lp->yes, lp->force, NULL)) { - log_error("Filesystem resize failed."); - return 0; + /* + * Possibly enable lv_meta extend if not already enabled. If lv_meta + * for a thin pool is not already being extended, and user requested + * extending the thin pool, then we may need to automatically include + * extending lv_meta in addition to lv_main (data), so that the + * metadata size is sufficient for the extended data size. + * + * If specific PVs were named to extend, this is taken to mean that + * only the thin pool data should be extended (using those PVs), and + * the thin pool metadata should not be automatically extended (since + * it would likely want to be extended using different PVs.) + */ + if (lv_is_thin_pool(lv_top) && is_extend && lv_main && !lv_meta && (&vg->pvs == lp->pvh)) { + struct lv_segment *tpseg = first_seg(lv_top); + uint64_t meta_size = estimate_thin_pool_metadata_size(lp->extents, vg->extent_size, tpseg->chunk_size); + if (meta_size > tpseg->metadata_lv->size) { + log_verbose("Extending thin pool metadata to %llu for larger data", (unsigned long long)meta_size); + lv_meta = tpseg->metadata_lv; + lp_meta = *lp; + _setup_params_for_extend_metadata(lv_meta, &lp_meta); + lp_meta.size = meta_size; + lp_meta.sign = SIGN_NONE; + /* meta may have a layer over it */ + lv_meta_layer = lv_meta; + lv_meta = _get_resizable_layer_lv(lv_meta_layer); + if (lv_meta == lv_meta_layer) + lv_meta_layer = NULL; } } - if (!lp->extents && (!aux_lv || !aux_lp.extents)) { - lp->extents = lv->le_count; - goto out; /* Nothing to do */ + /* + * Set a new size for lv_meta (extend only.) + */ + if (lv_meta) { + /* sets lp extents and lp resize */ + if (!_lv_resize_adjust_size(lv_meta, &lp_meta, &meta_size_matches)) + return_0; + /* sanity check the result of adjust_size */ + if (lp_meta.extents == 0) + return_0; + /* adjust_size resolves lp_meta.resize to EXTEND|REDUCE */ + /* _lv_resize_check_type errors if resize is EXTEND for thin meta */ + if (!_lv_resize_check_type(lv_meta, &lp_meta)) + return_0; } - if (lv_is_thin_pool(lock_lv) && /* Lock holder is thin-pool */ - !lv_is_active(lock_lv)) { + /* + * No resizing is needed. + */ + if ((main_size_matches && meta_size_matches) || + (main_size_matches && !lv_meta) || + (meta_size_matches && !lv_main)) { + log_error("No size change."); + return 0; + } + /* + * Active 'hidden' -tpool can be waiting for resize, but the pool LV + * itself might be inactive. Here plain suspend/resume would not work. + * So active temporarily pool LV (with on disk metadata) then use + * suspend and resume and deactivate pool LV, instead of searching for + * an active thin volume. + */ + if (lv_is_thin_pool(lv_top) && !lv_is_active(lv_top)) { if (!activation()) { - log_error("Cannot resize %s without using " - "device-mapper kernel driver.", - display_lvname(lock_lv)); - return 0; + log_error("Cannot resize %s without using device-mapper kernel driver.", + display_lvname(lv_top)); + return_0; } - /* - * Active 'hidden' -tpool can be waiting for resize, but the - * pool LV itself might be inactive. - * Here plain suspend/resume would not work. - * So active temporarily pool LV (with on disk metadata) - * then use suspend and resume and deactivate pool LV, - * instead of searching for an active thin volume. - */ - if (!activate_lv(cmd, lock_lv)) { - log_error("Failed to activate %s.", display_lvname(lock_lv)); - return 0; + if (!activate_lv(cmd, lv_top)) { + log_error("Failed to activate %s.", display_lvname(lv_top)); + return_0; } - activated = 1; } - /* Send DISCARD/TRIM to reduced area of VDO volumes - * TODO: enable thin and provide - * TODO2: we need polling method */ - if ((lp->resize == LV_REDUCE) && lv_is_vdo(lv)) { - if (dm_snprintf(name, sizeof(name), "%s%s/%s", lv->vg->cmd->dev_dir, - lv->vg->name, lv->name) < 0) { - log_error("Name too long - device not discarded (%s)", lv->name); - return 0; - } - - if (!(dev = dev_cache_get(lv->vg->cmd, name, NULL))) { - log_error("%s: not found: device not discarded.", name); - return 0; - } + /* + * If the LV is locked due to being active, this lock call is a no-op. + * Otherwise, this acquires a transient lock on the lv (not PERSISTENT) + */ + if (!lockd_lv_resize(cmd, lv_top, "ex", 0, lp)) + goto_out; - if (!dev_discard_max_bytes(cmd->dev_types, dev) || - !dev_discard_granularity(cmd->dev_types, dev)) { - log_error("%s: max bytes and granularity query fails.", name); - dev_destroy_file(dev); - return 0; + /* + * Check the file system, and shrink the fs before reducing lv. + * TODO: libblkid fs type, fs last_block, mount state, + * unlock vg, mount|unmount if needed, fork fs shrink, + * lock vg, rescan devs, recheck fs type last_block. + * (at end mount|unmount if needed to restore initial state.) + */ + if (lp->resizefs && !lp->nofsck && + !_fsadm_cmd(FSADM_CMD_CHECK, lv_top, 0, lp->yes, lp->force, &status)) { + if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) { + log_error("Filesystem check failed."); + goto out; } + /* some filesystems support online resize */ + } + if (lp->resizefs && is_reduce && + !_fsadm_cmd(FSADM_CMD_RESIZE, lv_top, lp->extents, lp->yes, lp->force, NULL)) { + log_error("Filesystem resize failed."); + goto out; + } - log_warn("WARNING: %s: Discarding %s at offset " FMTu64 ", please wait...", - name, display_size(cmd, (uint64_t)(lv->le_count - lp->extents) * vg->extent_size), - ((uint64_t)lp->extents * vg->extent_size) << SECTOR_SHIFT); - - if (!dev_discard_blocks(dev, ((uint64_t)lp->extents * vg->extent_size) << SECTOR_SHIFT, - ((uint64_t)(lv->le_count - lp->extents) * vg->extent_size) << SECTOR_SHIFT)) { - log_error("%s: discarding failed.", name); - dev_destroy_file(dev); - return 0; - } + /* + * Send DISCARD/TRIM to reduced area of VDO volumes + * TODO: enable thin and provide + * TODO2: we need polling method + */ + if (is_reduce && lv_is_vdo(lv_top) && !_lv_reduce_vdo_discard(cmd, lv_top, lp)) + goto_out; - dev_destroy_file(dev); - } /* - * If the LV is locked from activation, this lock call is a no-op. - * Otherwise, this acquires a transient lock on the lv (not PERSISTENT). + * Remove any striped raid reshape space for LV resizing (not common). */ - if (!lockd_lv_resize(cmd, lock_lv, "ex", 0, lp)) + if (lv_meta && first_seg(lv_meta)->reshape_len && !lv_raid_free_reshape_space(lv_meta)) + return_0; + if (lv_main && first_seg(lv_main)->reshape_len && !lv_raid_free_reshape_space(lv_main)) return_0; - /* Remove any striped raid reshape space for LV resizing */ - if (aux_lv && first_seg(aux_lv)->reshape_len) - if (!lv_raid_free_reshape_space(aux_lv)) - return_0; - if (first_seg(resizable_layer_lv)->reshape_len) - if (!lv_raid_free_reshape_space(resizable_layer_lv)) - return_0; - - if (aux_lv) { - if (!_lvresize_volume(aux_lv, &aux_lp, pvh)) - goto_bad; + /* + * The core of the actual lv resizing. + * Allocate or free extents in the VG, adjust LV segments to reflect + * new requested size, write VG metadata, reload the dm device stack + * (reload from the top LV.) Do lv_meta first. + * When extending lv_meta, also extend (or create) the pool's spare + * meta lv to match the size of lv_meta (only do this when the + * command is not limited to allocating from specific PVs.) + */ - /* store vg on disk(s) */ - if (aux_lp.size_changed && !lv_update_and_reload(lock_lv)) - goto_bad; - } + if (!lv_meta) + goto do_main; + if (!_lv_resize_volume(lv_meta, &lp_meta, lp->pvh)) + goto_out; + if (!lp_meta.size_changed) + goto do_main; + if ((&vg->pvs == lp->pvh) && !handle_pool_metadata_spare(vg, 0, lp->pvh, 1)) + stack; + if (!lv_update_and_reload(lv_top)) + goto_out; + log_debug("Resized thin pool metadata %s to %u extents.", display_lvname(lv_meta), lp_meta.extents); - if (!_lvresize_volume(resizable_layer_lv, lp, pvh)) - goto_bad; + do_main: - /* store vg on disk(s) */ + if (!lv_main) + goto end_main; + if (!_lv_resize_volume(lv_main, lp, lp->pvh)) + goto_out; if (!lp->size_changed) - goto out; /* No table reload needed */ + goto_out; + if (!lv_update_and_reload(lv_top)) + goto_out; + log_debug("Resized %s to %u extents.", display_lvname(lv_main), lp->extents); - if (!lv_update_and_reload(lock_lv)) - goto_bad; + end_main: - if (lv_is_cow_covering_origin(lv)) - if (!monitor_dev_for_events(cmd, lv, 0, 0)) + /* + * other maintenance: + * - update lvm pool metadata (drop messages). + * - print warnings about overprovisioning. + * - stop monitoring cow snapshot larger than origin + */ + if (lv_is_thin_pool(lv_top)) { + if (!update_thin_pool_lv(lv_top, 1)) + goto_out; + if (is_extend) + thin_pool_check_overprovisioning(lv_top); + } + if (lv_main && lv_is_cow_covering_origin(lv_main)) { + if (!monitor_dev_for_events(cmd, lv_main, 0, 0)) stack; - - if (lv_is_thin_pool(lock_lv)) { - /* Update lvm pool metadata (drop messages). */ - if (!update_thin_pool_lv(lock_lv, 1)) - goto_bad; } - /* Check for over provisioning when extended */ - if ((lp->resize == LV_EXTEND) && lv_is_thin_type(lv)) - thin_pool_check_overprovisioning(lv); - -out: - log_print_unless_silent("Logical volume %s successfully resized.", - display_lvname(lv)); - - if (lp->resizefs && (lp->resize == LV_EXTEND) && - !_fsadm_cmd(FSADM_CMD_RESIZE, lv, lp->extents, lp->yes, lp->force, NULL)) - goto_bad; + /* + * Extend the file system. + * TODO: libblkid fs type, mount state, + * unlock vg, mount|unmount if needed, fork fs grow, + * mount|unmount if needed to restore initial state. + */ + if (lp->resizefs && is_extend && + !_fsadm_cmd(FSADM_CMD_RESIZE, lv_top, lp->extents, lp->yes, lp->force, NULL)) { + log_warn("Filesystem resize failed."); + goto out; + } ret = 1; -bad: - if (activated) { - if (!sync_local_dev_names(lock_lv->vg->cmd)) { - log_error("Failed to sync local devices before deactivating LV %s.", - display_lvname(lock_lv)); - return 0; - } - if (!deactivate_lv(cmd, lock_lv)) { - log_error("Problem deactivating %s.", display_lvname(lock_lv)); - ret = 0; - } + out: + if (activated) { + if (!sync_local_dev_names(cmd)) + stack; + if (!deactivate_lv(cmd, lv_top)) + log_warn("Problem deactivating %s.", display_lvname(lv_top)); } return ret; diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index deaf3f30c..048b7553f 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -297,6 +297,9 @@ int lv_layout_and_role(struct dm_pool *mem, const struct logical_volume *lv, struct dm_list **layout, struct dm_list **role); +int lv_is_linear(struct logical_volume *lv); +int lv_is_striped(struct logical_volume *lv); + /* Ordered list - see lv_manip.c */ typedef enum { AREA_UNASSIGNED, @@ -661,42 +664,33 @@ struct pvcreate_params { }; struct lvresize_params { - int argc; - char **argv; - - const char *vg_name; /* only-used when VG is not yet opened (in /tools) */ - const char *lv_name; - - const struct segment_type *segtype; - - uint64_t poolmetadata_size; - sign_t poolmetadata_sign; - - /* Per LV applied parameters */ - enum { LV_ANY = 0, LV_REDUCE = 1, LV_EXTEND = 2 } resize; - - int use_policies; - alloc_policy_t alloc; int yes; int force; int nosync; int nofsck; int resizefs; + int use_policies; + const struct segment_type *segtype; unsigned mirrors; uint32_t stripes; uint64_t stripe_size; - uint32_t extents; uint64_t size; + uint32_t extents; sign_t sign; - percent_type_t percent; + percent_type_t percent; /* the type of percentage, not a value */ + uint32_t percent_value; /* 0 - 100 */ + uint64_t poolmetadata_size; + sign_t poolmetadata_sign; + uint32_t policy_percent_main; + uint32_t policy_percent_meta; int approx_alloc; int extents_are_pes; /* Is 'extents' counting PEs or LEs? */ @@ -705,6 +699,8 @@ struct lvresize_params { const char *lockopt; char *lockd_lv_refresh_path; /* set during resize to use for refresh at the end */ char *lockd_lv_refresh_uuid; /* set during resize to use for refresh at the end */ + + struct dm_list *pvh; /* list of pvs to use */ }; void pvcreate_params_set_defaults(struct pvcreate_params *pp); @@ -745,9 +741,10 @@ int vgs_are_compatible(struct cmd_context *cmd, struct volume_group *vg_to); uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname); -int lv_resize(struct logical_volume *lv, - struct lvresize_params *lp, - struct dm_list *pvh); +int lv_resize(struct cmd_context *cmd, struct logical_volume *lv, + struct lvresize_params *lp); +int lv_extend_policy_calculate_percent(struct logical_volume *lv, + uint32_t *amount, uint32_t *meta_amount); struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name, const char *vgid, uint32_t read_flags, uint32_t lockd_state, diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index 1cda1888f..95f25eef8 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -5302,3 +5302,15 @@ int get_visible_lvs_using_pv(struct cmd_context *cmd, struct volume_group *vg, s return 1; } +int lv_is_linear(struct logical_volume *lv) +{ + struct lv_segment *seg = first_seg(lv); + return segtype_is_linear(seg->segtype); +} + +int lv_is_striped(struct logical_volume *lv) +{ + struct lv_segment *seg = first_seg(lv); + return segtype_is_striped(seg->segtype); +} + diff --git a/lib/metadata/pool_manip.c b/lib/metadata/pool_manip.c index bb4b5f115..a6bfc2deb 100644 --- a/lib/metadata/pool_manip.c +++ b/lib/metadata/pool_manip.c @@ -737,6 +737,7 @@ int handle_pool_metadata_spare(struct volume_group *vg, uint32_t extents, extents = MAX_SIZE; if (!lv) { + log_debug("Adding new pool metadata spare %u extents.", extents); if (!_alloc_pool_metadata_spare(vg, extents, pvh)) return_0; @@ -746,6 +747,8 @@ int handle_pool_metadata_spare(struct volume_group *vg, uint32_t extents, seg = last_seg(lv); seg_mirrors = lv_mirror_count(lv); + log_debug("Extending pool metadata spare from %u to %u extents.", + lv->le_count, extents); /* Check spare LV is big enough and preserve segtype */ if ((lv->le_count < extents) && seg && !lv_extend(lv, seg->segtype, diff --git a/test/shell/lvextend-thin-full.sh b/test/shell/lvextend-thin-full.sh index 263ab2fb9..69d15e3b0 100644 --- a/test/shell/lvextend-thin-full.sh +++ b/test/shell/lvextend-thin-full.sh @@ -41,16 +41,15 @@ lvcreate -L2M -n $lv1 $vg "$LVM_TEST_THIN_RESTORE_CMD" -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1" lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1 -# Cannot resize if set to 0% -not lvextend --use-policies --config 'activation{thin_pool_autoextend_percent = 0}' $vg/pool 2>&1 | tee err -grep "0%" err - -# Locally active LV is needed +# active thin pool is needed to use policy not lvextend --use-policies $vg/pool 2>&1 | tee err -grep "locally" err lvchange -ay $vg +# Cannot resize if set to 0% +not lvextend --use-policies --config 'activation{thin_pool_autoextend_percent = 0}' $vg/pool 2>&1 | tee err +grep "0%" err + # Creation of new LV is not allowed when thinpool is over threshold not lvcreate -V10 $vg/pool diff --git a/test/shell/lvresize-full.sh b/test/shell/lvresize-full.sh index dcf93a7b2..1ad9f32b1 100644 --- a/test/shell/lvresize-full.sh +++ b/test/shell/lvresize-full.sh @@ -37,15 +37,19 @@ lvs -a $vg "$MKFS" "$lvdev" # this should resolve to resize to same actual size -lvreduce -r -f -l-100%FREE $vg/$lv1 +not lvreduce -l-100%FREE $vg/$lv1 +not lvreduce -r -f -l-100%FREE $vg/$lv1 "$FSCK" -n "$lvdev" # size should remain the same -lvextend -r -f -l+100%FREE $vg/$lv1 +# lvresize fails with same result with or without -r +not lvextend -l+100%FREE $vg/$lv1 +not lvextend -r -f -l+100%FREE $vg/$lv1 "$FSCK" -n "$lvdev" #lvchange -an $vg/$lv1 -lvresize -r -f -l+100%FREE $vg/$lv1 +not lvresize -l+100%FREE $vg/$lv1 +not lvresize -r -f -l+100%FREE $vg/$lv1 "$FSCK" -n "$lvdev" # Check there is really file system resize happening @@ -55,7 +59,8 @@ lvresize -r -f -l+100%FREE $vg/$lv1 grep "20000 blocks" out SIZE=$(get lv_field $vg/$lv1 size) -lvresize -r -f -l-100%FREE $vg/$lv1 +not lvresize -l-100%FREE $vg/$lv1 +not lvresize -r -f -l-100%FREE $vg/$lv1 test "$SIZE" = "$(get lv_field $vg/$lv1 size)" "$FSCK" -n "$lvdev" | tee out diff --git a/tools/command-lines.in b/tools/command-lines.in index 6e9e7bdea..0cb9425c1 100644 --- a/tools/command-lines.in +++ b/tools/command-lines.in @@ -1382,7 +1382,7 @@ OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number, --stripes Number, --stripesize SizeKB, --poolmetadatasize PSizeMB, --type SegType OP: PV ... -ID: lvextend_by_size +ID: lvextend_size DESC: Extend an LV by a specified size. lvextend LV PV ... @@ -1390,25 +1390,25 @@ OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number, --nofsck, --nosync, --noudevsync, --reportformat ReportFmt, --resizefs, --stripes Number, --stripesize SizeKB, --type SegType -ID: lvextend_by_pv +ID: lvextend_pv DESC: Extend an LV by specified PV extents. -lvextend --poolmetadatasize PSizeMB LV_thinpool +lvextend --poolmetadatasize PSizeMB LV_thinpool_linear OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number, --nofsck, --nosync, --noudevsync, --reportformat ReportFmt, --stripes Number, --stripesize SizeKB, --type SegType OP: PV ... -ID: lvextend_pool_metadata_by_size +ID: lvextend_pool_metadata DESC: Extend a pool metadata SubLV by a specified size. -lvextend --usepolicies LV_thinpool_snapshot +lvextend --usepolicies LV_snapshot_thinpool_vdopool OO: --alloc Alloc, --autobackup Bool, --force, --mirrors Number, --nofsck, --nosync, --noudevsync, --reportformat ReportFmt, --resizefs, --type SegType OP: PV ... -ID: lvextend_by_policy +ID: lvextend_policy DESC: Extend an LV according to a predefined policy. --- @@ -1456,7 +1456,7 @@ DESC: Remove the devices file entry for the given PVID. lvreduce --size NSizeMB LV OO: --autobackup Bool, --force, --nofsck, --noudevsync, --reportformat ReportFmt, --resizefs -ID: lvreduce_general +ID: lvreduce_size --- @@ -1487,7 +1487,7 @@ OO: --alloc Alloc, --autobackup Bool, --force, --stripes Number, --stripesize SizeKB, --poolmetadatasize PSizeMB, --type SegType OP: PV ... -ID: lvresize_by_size +ID: lvresize_size DESC: Resize an LV by a specified size. lvresize LV PV ... @@ -1495,7 +1495,7 @@ OO: --alloc Alloc, --autobackup Bool, --force, --nofsck, --nosync, --noudevsync, --reportformat ReportFmt, --resizefs, --stripes Number, --stripesize SizeKB, --type SegType -ID: lvresize_by_pv +ID: lvresize_pv DESC: Resize an LV by specified PV extents. lvresize --poolmetadatasize PSizeMB LV_thinpool @@ -1504,7 +1504,7 @@ OO: --alloc Alloc, --autobackup Bool, --force, --reportformat ReportFmt, --stripes Number, --stripesize SizeKB, --type SegType OP: PV ... -ID: lvresize_pool_metadata_by_size +ID: lvresize_pool_metadata DESC: Resize a pool metadata SubLV by a specified size. --- diff --git a/tools/lvextend.c b/tools/lvextend.c index 865bb965e..e915a95cf 100644 --- a/tools/lvextend.c +++ b/tools/lvextend.c @@ -17,5 +17,5 @@ int lvextend(struct cmd_context *cmd, int argc, char **argv) { - return lvresize(cmd, argc, argv); + return lvresize_cmd(cmd, argc, argv); } diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c index ba3ca220b..a5bb6a5c5 100644 --- a/tools/lvmcmdline.c +++ b/tools/lvmcmdline.c @@ -165,6 +165,16 @@ static const struct command_function _command_functions[CMD_COUNT] = { { pvscan_display_CMD, pvscan_display_cmd }, { pvscan_cache_CMD, pvscan_cache_cmd }, + + /* lvextend/lvreduce/lvresize */ + { lvextend_policy_CMD, lvextend_policy_cmd }, + { lvextend_pool_metadata_CMD, lvresize_cmd }, + { lvresize_pool_metadata_CMD, lvresize_cmd }, + { lvextend_pv_CMD, lvresize_cmd }, + { lvresize_pv_CMD, lvresize_cmd }, + { lvextend_size_CMD, lvresize_cmd }, + { lvreduce_size_CMD, lvresize_cmd }, + { lvresize_size_CMD, lvresize_cmd }, }; @@ -3153,6 +3163,9 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) if (!(cmd->command = _find_command(cmd, cmd->name, &argc, argv))) return EINVALID_CMD_LINE; + /* avoid this by letting lib code use cmd->command */ + cmd->command_enum = cmd->command->command_enum; + /* * If option --foo is set which is listed in IO (ignore option) in * command-lines.in, then unset foo. Commands won't usually use an diff --git a/tools/lvreduce.c b/tools/lvreduce.c index 7b5d1583a..721d7f1b9 100644 --- a/tools/lvreduce.c +++ b/tools/lvreduce.c @@ -17,5 +17,5 @@ int lvreduce(struct cmd_context *cmd, int argc, char **argv) { - return lvresize(cmd, argc, argv); + return lvresize_cmd(cmd, argc, argv); } diff --git a/tools/lvresize.c b/tools/lvresize.c index f39f03a40..a7148e52c 100644 --- a/tools/lvresize.c +++ b/tools/lvresize.c @@ -15,119 +15,110 @@ #include "tools.h" -static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv, - struct lvresize_params *lp) +static int _lvresize_params(struct cmd_context *cmd, struct lvresize_params *lp) { - const char *cmd_name = command_name(cmd); const char *type_str = arg_str_value(cmd, type_ARG, NULL); int only_linear = 0; + int set_extents_and_size = 0; - if (type_str) { - if (!strcmp(type_str, "linear")) { - type_str = "striped"; - only_linear = 1; /* User requested linear only target */ - } + memset(lp, 0, sizeof(struct lvresize_params)); - if (!(lp->segtype = get_segtype_from_string(cmd, type_str))) - return_0; - } + switch (cmd->command->command_enum) { + case lvextend_policy_CMD: + lp->resize = LV_EXTEND; + lp->size = 0; + lp->extents = 0; + lp->percent = PERCENT_LV; + lp->sign = SIGN_PLUS; + lp->poolmetadata_size = 0; + lp->use_policies = 1; + break; + + case lvextend_pool_metadata_CMD: + case lvresize_pool_metadata_CMD: + lp->resize = LV_EXTEND; + lp->size = 0; + lp->extents = 0; + lp->percent = PERCENT_NONE; + lp->sign = SIGN_NONE; + lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0); + lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE); + break; + + case lvextend_pv_CMD: + case lvresize_pv_CMD: + lp->resize = LV_EXTEND; + lp->size = 0; + lp->extents = 0; + lp->percent_value = 100; + lp->percent = PERCENT_PVS; + lp->sign = SIGN_PLUS; + lp->poolmetadata_size = 0; + lp->resizefs = arg_is_set(cmd, resizefs_ARG); + lp->nofsck = arg_is_set(cmd, nofsck_ARG); + break; + + case lvextend_size_CMD: + lp->resize = LV_EXTEND; + lp->resizefs = arg_is_set(cmd, resizefs_ARG); + lp->nofsck = arg_is_set(cmd, nofsck_ARG); + if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0))) + lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE); + set_extents_and_size = 1; + break; - if (!strcmp(cmd_name, "lvreduce")) + case lvreduce_size_CMD: lp->resize = LV_REDUCE; - else if (!strcmp(cmd_name, "lvextend")) - lp->resize = LV_EXTEND; - else + lp->poolmetadata_size = 0; + lp->resizefs = arg_is_set(cmd, resizefs_ARG); + lp->nofsck = arg_is_set(cmd, nofsck_ARG); + set_extents_and_size = 1; + break; + + case lvresize_size_CMD: lp->resize = LV_ANY; + lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0); + lp->resizefs = arg_is_set(cmd, resizefs_ARG); + lp->nofsck = arg_is_set(cmd, nofsck_ARG); + if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0))) + lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE); + set_extents_and_size = 1; + break; - lp->sign = lp->poolmetadata_sign = SIGN_NONE; - - if ((lp->use_policies = arg_is_set(cmd, usepolicies_ARG))) { - /* do nothing; lv_resize will handle --use-policies itself */ - if (arg_from_list_is_set(cmd, NULL, - chunksize_ARG, extents_ARG, - poolmetadatasize_ARG, - regionsize_ARG, - size_ARG, - stripes_ARG, stripesize_ARG, - -1)) - log_print_unless_silent("Ignoring size parameters with --use-policies."); - } else { - /* - * Allow omission of extents and size if the user has given us - * one or more PVs. Most likely, the intent was "resize this - * LV the best you can with these PVs" - * If only --poolmetadatasize is specified with list of PVs, - * then metadata will be extended there. - */ + default: + log_error(INTERNAL_ERROR "unknown lvresize type"); + return 0; + }; + + if (set_extents_and_size) { if ((lp->extents = arg_uint_value(cmd, extents_ARG, 0))) { - lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE); + lp->sign = arg_sign_value(cmd, extents_ARG, 0); lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE); } - - if (arg_from_list_is_zero(cmd, "may not be zero", - chunksize_ARG, extents_ARG, - poolmetadatasize_ARG, - regionsize_ARG, - size_ARG, - stripes_ARG, stripesize_ARG, - virtualsize_ARG, - -1)) - return_0; - - if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0))) { - lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE); - if (lp->poolmetadata_sign == SIGN_MINUS) { - log_error("Can't reduce pool metadata size."); - return 0; - } - } - if ((lp->size = arg_uint64_value(cmd, size_ARG, 0))) { - lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE); + lp->sign = arg_sign_value(cmd, size_ARG, 0); lp->percent = PERCENT_NONE; } - if (lp->size && lp->extents) { log_error("Please specify either size or extents but not both."); return 0; } - - if (!lp->extents && - !lp->size && - !lp->poolmetadata_size && - (argc >= 2)) { - lp->extents = 100; - lp->percent = PERCENT_PVS; - lp->sign = SIGN_PLUS; - } } - if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) { - log_error("Negative argument not permitted - use lvreduce."); - return 0; - } + lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, 0); + lp->yes = arg_is_set(cmd, yes_ARG); + lp->force = arg_is_set(cmd, force_ARG), + lp->nosync = arg_is_set(cmd, nosync_ARG); + lp->lockopt = arg_str_value(cmd, lockopt_ARG, NULL); - if (lp->resize == LV_REDUCE && - ((lp->sign == SIGN_PLUS) || - (lp->poolmetadata_sign == SIGN_PLUS))) { - log_error("Positive sign not permitted - use lvextend."); - return 0; - } - - if (!argc) { - log_error("Please provide the logical volume name."); - return 0; - } - - lp->lv_name = argv[0]; - - if (!validate_lvname_param(cmd, &lp->vg_name, &lp->lv_name)) - return_0; + if (type_str) { + if (!strcmp(type_str, "linear")) { + type_str = "striped"; + only_linear = 1; /* User requested linear only target */ + } - /* Check for $LVM_VG_NAME */ - if (!lp->vg_name && !(lp->vg_name = extract_vgname(cmd, NULL))) { - log_error("Please specify a logical volume path."); - return 0; + if (!(lp->segtype = get_segtype_from_string(cmd, type_str))) + return_0; } if (arg_is_set(cmd, mirrors_ARG)) { @@ -156,66 +147,150 @@ static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv, return 0; } - lp->argc = --argc; - lp->argv = ++argv; - return 1; } -static int _lvresize_single(struct cmd_context *cmd, const char *vg_name, - struct volume_group *vg, struct processing_handle *handle) +/* + * lvextend --use-policies is usually called by dmeventd, as a method of + * "auto extending" an LV as it's used. It checks how full a snapshot cow or + * thin pool is, and extends it if it's too full, based on threshold settings + * in lvm.conf for when to auto extend it. + * + * The extension of a thin pool LV can involve extending either the data sub + * LV, the metadata sub LV, or both, so there may be two LVs extended here. + */ +static int _lv_extend_policy(struct cmd_context *cmd, struct logical_volume *lv, + struct lvresize_params *lp, int *skipped) { - struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle; - struct dm_list *pvh; - struct logical_volume *lv; - int ret = ECMD_FAILED; - - /* Does LV exist? */ - if (!(lv = find_lv(vg, lp->lv_name))) { - log_error("Logical volume %s not found in volume group %s.", - lp->lv_name, vg->name); - goto out; + struct lvresize_params lp_meta; + uint32_t percent_main = 0; + uint32_t percent_meta = 0; + int is_active; + + memset(&lp_meta, 0, sizeof(lp_meta)); + + if (!lv_is_cow(lv) && !lv_is_thin_pool(lv) && !lv_is_vdo_pool(lv)) { + log_error("lvextend policy is supported only for snapshot, thin pool and vdo pool volumes."); + *skipped = 1; + return 0; + } + + is_active = lv_is_active(lv); + + if (vg_is_shared(lv->vg) && !is_active) { + log_debug("lvextend policy requires LV to be active in a shared VG."); + *skipped = 1; + return 1; + } + + if (lv_is_thin_pool(lv) && !is_active) { + log_error("lvextend using policy requires the thin pool to be active."); + return 0; } - if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc, lp->argv, 1) : &vg->pvs)) - goto_out; + /* + * Calculate the percent of extents to extend the LV based on current + * usage info from the kernel and policy settings from lvm.conf, e.g. + * autoextend_threshold, autoextend_percent. For thin pools, both the + * thin pool data LV and thin pool metadata LV may need to be extended. + * In this case, percent_main is the amount to extend the data LV, and + * percent_meta is the amount to extend the metadata LV. + */ + if (!lv_extend_policy_calculate_percent(lv, &percent_main, &percent_meta)) + return_0; + + if (!percent_main && !percent_meta) { + log_debug("lvextend policy not needed."); + *skipped = 1; + return 1; + } - if (!lv_resize(lv, lp, pvh)) - goto_out; + *skipped = 0; + lp->policy_percent_main = percent_main; + lp->policy_percent_meta = percent_meta; + + return lv_resize(cmd, lv, lp); +} + +static int _lvextend_policy_single(struct cmd_context *cmd, struct logical_volume *lv, + struct processing_handle *handle) +{ + struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle; + int skipped = 0; + + if (cmd->position_argc > 1) { + /* First pos arg is required LV, remaining are optional PVs. */ + if (!(lp->pvh = create_pv_list(cmd->mem, lv->vg, cmd->position_argc - 1, cmd->position_argv + 1, 0))) + return_ECMD_FAILED; + } else + lp->pvh = &lv->vg->pvs; + + if (!_lv_extend_policy(cmd, lv, lp, &skipped)) + return ECMD_FAILED; + + if (!skipped) + log_print_unless_silent("Logical volume %s successfully resized.", display_lvname(lv)); + return ECMD_PROCESSED; +} + +static int _lvresize_single(struct cmd_context *cmd, struct logical_volume *lv, + struct processing_handle *handle) +{ + struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle; + + if (cmd->position_argc > 1) { + /* First pos arg is required LV, remaining are optional PVs. */ + if (!(lp->pvh = create_pv_list(cmd->mem, lv->vg, cmd->position_argc - 1, cmd->position_argv + 1, 0))) + return_ECMD_FAILED; + } else + lp->pvh = &lv->vg->pvs; + + if (!lv_resize(cmd, lv, lp)) + return ECMD_FAILED; + + log_print_unless_silent("Logical volume %s successfully resized.", + display_lvname(lv)); + return ECMD_PROCESSED; +} + +int lvextend_policy_cmd(struct cmd_context *cmd, int argc, char **argv) +{ + struct processing_handle *handle; + struct lvresize_params lp; + int ret; + + if (!_lvresize_params(cmd, &lp)) + return EINVALID_CMD_LINE; + + if (!(handle = init_processing_handle(cmd, NULL))) + return ECMD_FAILED; + + handle->custom_handle = &lp; + + ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, + handle, NULL, &_lvextend_policy_single); + + destroy_processing_handle(cmd, handle); - ret = ECMD_PROCESSED; -out: return ret; } -int lvresize(struct cmd_context *cmd, int argc, char **argv) +int lvresize_cmd(struct cmd_context *cmd, int argc, char **argv) { struct processing_handle *handle; - struct lvresize_params lp = { - .alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, 0), - .yes = arg_is_set(cmd, yes_ARG), - .force = arg_is_set(cmd, force_ARG), - .nofsck = arg_is_set(cmd, nofsck_ARG), - .nosync = arg_is_set(cmd, nosync_ARG), - .resizefs = arg_is_set(cmd, resizefs_ARG), - .lockopt = arg_str_value(cmd, lockopt_ARG, NULL), - }; + struct lvresize_params lp; int ret; - if (!_lvresize_params(cmd, argc, argv, &lp)) { - stack; + if (!_lvresize_params(cmd, &lp)) return EINVALID_CMD_LINE; - } - if (!(handle = init_processing_handle(cmd, NULL))) { - log_error("Failed to initialize processing handle."); + if (!(handle = init_processing_handle(cmd, NULL))) return ECMD_FAILED; - } handle->custom_handle = &lp; - ret = process_each_vg(cmd, 0, NULL, lp.vg_name, NULL, READ_FOR_UPDATE, 0, handle, - &_lvresize_single); + ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, + handle, NULL, &_lvresize_single); destroy_processing_handle(cmd, handle); @@ -224,3 +299,16 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) return ret; } + +/* + * All lvresize command defs have their own function, + * so the generic function name is unused. + */ + +int lvresize(struct cmd_context *cmd, int argc, char **argv) +{ + log_error(INTERNAL_ERROR "Missing function for command definition %d:%s.", + cmd->command->command_index, cmd->command->command_id); + return ECMD_FAILED; +} + diff --git a/tools/tools.h b/tools/tools.h index 2636c22da..36da3bc7e 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -45,19 +45,18 @@ #include "lib/notify/lvmnotify.h" #include "lib/label/hints.h" +/* + * cmd_enum.h uses the generated cmds.h to create the enum with an ID + * for each command definition in command-lines.in. + */ +#include "lib/commands/cmd_enum.h" + #include #include #define CMD_LEN 256 #define MAX_ARGS 64 -/* define the enums for each unique ID in command defs in command-lines.in */ -enum { -#define cmd(a, b) a , -#include "cmds.h" -#undef cmd -}; - /* define the enums for the values accepted by command line --options, foo_VAL */ enum { #define val(a, b, c, d) a , @@ -296,4 +295,7 @@ int lvconvert_cachevol_attach_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle); +int lvresize_cmd(struct cmd_context *cmd, int argc, char **argv); +int lvextend_policy_cmd(struct cmd_context *cmd, int argc, char **argv); + #endif