]> sourceware.org Git - lvm2.git/commitdiff
lvresize: restructure code
authorDavid Teigland <teigland@redhat.com>
Fri, 9 Sep 2022 21:07:07 +0000 (16:07 -0500)
committerDavid Teigland <teigland@redhat.com>
Fri, 9 Sep 2022 21:18:55 +0000 (16:18 -0500)
Rewrite top level resize function to prepare for adding
new fs resizing.

15 files changed:
lib/commands/cmd_enum.h [new file with mode: 0644]
lib/commands/toolcontext.h
lib/locking/lvmlockd.c
lib/metadata/lv_manip.c
lib/metadata/metadata-exported.h
lib/metadata/metadata.c
lib/metadata/pool_manip.c
test/shell/lvextend-thin-full.sh
test/shell/lvresize-full.sh
tools/command-lines.in
tools/lvextend.c
tools/lvmcmdline.c
tools/lvreduce.c
tools/lvresize.c
tools/tools.h

diff --git a/lib/commands/cmd_enum.h b/lib/commands/cmd_enum.h
new file mode 100644 (file)
index 0000000..939af81
--- /dev/null
@@ -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
index 7a4979b33e5361ed82422524469ae4dd514e5b8f..eb2d1922b9550307b768b043d3249df46f24dd0a 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "lib/device/dev-cache.h"
 #include "lib/device/dev-type.h"
+#include "lib/commands/cmd_enum.h"
 
 #include <limits.h>
 
@@ -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;
index f0183db3dd7c387ba8ed27b5bab0b88dce8c4e28..2ef0900d47cc454b684c6f313e0a5c31ebfb4065 100644 (file)
@@ -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
index a9057830d65a66a5371fb53cf58fcff8604c66ed..e819db8d5c38dec722241917e637bd86d872c8f5 100644 (file)
@@ -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;
index deaf3f30c6d2f749c5d92d44a6970a5ca497490d..048b7553fb0beecff8fc1e9123fda3b5c9346fb0 100644 (file)
 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,
index 1cda1888f35698c43a0dbc0ca4d8693730ad9a0f..95f25eef8f0828d8b7825db609fa9a0130470f3e 100644 (file)
@@ -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);
+}
+
index bb4b5f11517d427528ff1c84865a3129ca2dee9d..a6bfc2deb07a92823f3f9baabe614512af916f7d 100644 (file)
@@ -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,
index 263ab2fb96dd79e61a6136450bd21b788601a5c4..69d15e3b0a754998bdd78f7c1f002bae67b2e4dd 100644 (file)
@@ -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
 
index dcf93a7b2ad2b5da6552b2d975bcedb81f8bee46..1ad9f32b16db6d203caf5950c91625b2ea00db6c 100644 (file)
@@ -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
index 6e9e7bdeabe5fabd03342bbf83007a59dce9fd00..0cb9425c1865d93d0f5f4730d81efca709762643 100644 (file)
@@ -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.
 
 ---
index 865bb965ef7ca944ca41f5ca5a50f8ec38c88411..e915a95cffba2af4ae971b7148193ad0f84c5949 100644 (file)
@@ -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);
 }
index ba3ca220b482659c269d3aa2710d034e4e780e91..a5bb6a5c5fe7cee75e3df22c9fca275a7559bf0d 100644 (file)
@@ -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
index 7b5d1583a97b0ab840a7bdad5d68cd75d9ef88cf..721d7f1b9fa98280fb3cef4bd36fead9efab4f66 100644 (file)
@@ -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);
 }
index f39f03a40c8455d958e68a2addf65f68c001cea0..a7148e52c5845706062a4c83a5573e9478cd814c 100644 (file)
 
 #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;
+}
+
index 2636c22dac00a502d461e35940675f64200bb824..36da3bc7e78dadbed5902a9c7899849a4aaaadbc 100644 (file)
 #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 <ctype.h>
 #include <sys/types.h>
 
 #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
This page took 0.091384 seconds and 5 git commands to generate.